spring social
在本博客中,我将介绍您需要在应用程序的一两个页面上显示用户的Facebook或其他软件即服务(SaaS)提供程序数据的情况。 这里的想法是尝试演示最小的和最简单的操作,您可以将Spring Social添加到需要用户登录Facebook或其他SaaS提供商的应用程序中。
创建应用
要创建该应用程序,第一步是使用SpringSource Toolkit仪表板的模板部分创建一个基本的Spring MVC项目。 这提供了一个Web应用程序,可帮助您入门。
下一步是通过添加以下依赖项来设置pom.xml :
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>${org.springframework.security.crypto-version}</version>
</dependency>
<!-- Spring Social -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-core</artifactId>
<version>${spring-social.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-web</artifactId>
<version>${spring-social.version}</version>
</dependency>
<!-- Facebook API -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
<version>${org.springframework.social-facebook-version}</version>
</dependency>
<!-- JdbcUserConfiguration -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.159</version>
</dependency>
<!-- CGLIB, only required and used for @Configuration usage: could be removed in future release of Spring -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
…显然,您还需要在文件的%lt; properties />部分中添加以下内容:
<spring-social.version>1.0.2.RELEASE</spring-social.version>
<org.springframework.social-facebook-version>1.0.1.RELEASE</org.springframework.social-facebook-version>
<org.springframework.security.crypto-version>3.1.0.RELEASE</org.springframework.security.crypto-version>
您会注意到,我为spring-security-crypto添加了一个特定的pom条目:这是因为我正在使用Spring 3.0.6。 在Spring 3.1.x中,它已成为核心库的一部分。
唯一要注意的一点是,还依赖于spring-jdbc和h2 。 这是因为Spring的UserConnectionRepository默认实现: JdbcUsersConnectionRepository使用它们,因此即使此应用程序不对数据库持久化任何东西(据我所知),它们也是必需的。
班级
社交编码功能包括四个类(其中一个是我从Keith Donald的Spring Social Quick Start Sample代码中摘录的):
- FacebookPostsController
- 社会背景
- Facebook配置
- UserCookieGenerator
FacebookPostsController是应用程序的业务端,负责获取用户的Facebook数据并将其推入模型中以供显示。
@Controller
public class FacebookPostsController {
private static final Logger logger = LoggerFactory.getLogger(FacebookPostsController.class);
private final SocialContext socialContext;
@Autowired
public FacebookPostsController(SocialContext socialContext) {
this.socialContext = socialContext;
}
@RequestMapping(value = 'posts', method = RequestMethod.GET)
public String showPostsForUser(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
String nextView;
if (socialContext.isSignedIn(request, response)) {
List<Post> posts = retrievePosts();
model.addAttribute('posts', posts);
nextView = 'show-posts';
} else {
nextView = 'signin';
}
return nextView;
}
private List<Post> retrievePosts() {
Facebook facebook = socialContext.getFacebook();
FeedOperations feedOps = facebook.feedOperations();
List<Post> posts = feedOps.getHomeFeed();
logger.info('Retrieved ' + posts.size() + ' posts from the Facebook authenticated user');
return posts;
}
}
如您所见,从高级的角度来看,我们要实现的目标的逻辑非常简单:
IF user is signed in THEN
read Facebook data,
display Facebook data
ELSE
ask user to sign in
when user has signed in, go back to the beginning
END IF
FacebookPostsController将处理登录逻辑的任务委托给SocialContext类。 您可能会猜到,我从Spring真正有用的ApplicationContext中得到了此类的想法。 这里的想法是,有一个类负责将您的应用程序粘贴到Spring Social。
public class SocialContext implements ConnectionSignUp, SignInAdapter {
/**
* Use a random number generator to generate IDs to avoid cookie clashes
* between server restarts
*/
private static Random rand;
/**
* Manage cookies - Use cookies to remember state between calls to the
* server(s)
*/
private final UserCookieGenerator userCookieGenerator;
/** Store the user id between calls to the server */
private static final ThreadLocal<String> currentUser = new ThreadLocal<String>();
private final UsersConnectionRepository connectionRepository;
private final Facebook facebook;
public SocialContext(UsersConnectionRepository connectionRepository, UserCookieGenerator userCookieGenerator,
Facebook facebook) {
this.connectionRepository = connectionRepository;
this.userCookieGenerator = userCookieGenerator;
this.facebook = facebook;
rand = new Random(Calendar.getInstance().getTimeInMillis());
}
@Override
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
userCookieGenerator.addCookie(userId, request.getNativeResponse(HttpServletResponse.class));
return null;
}
@Override
public String execute(Connection<?> connection) {
return Long.toString(rand.nextLong());
}
public boolean isSignedIn(HttpServletRequest request, HttpServletResponse response) {
boolean retVal = false;
String userId = userCookieGenerator.readCookieValue(request);
if (isValidId(userId)) {
if (isConnectedFacebookUser(userId)) {
retVal = true;
} else {
userCookieGenerator.removeCookie(response);
}
}
currentUser.set(userId);
return retVal;
}
private boolean isValidId(String id) {
return isNotNull(id) && (id.length() > 0);
}
private boolean isNotNull(Object obj) {
return obj != null;
}
private boolean isConnectedFacebookUser(String userId) {
ConnectionRepository connectionRepo = connectionRepository.createConnectionRepository(userId);
Connection<Facebook> facebookConnection = connectionRepo.findPrimaryConnection(Facebook.class);
return facebookConnection != null;
}
public String getUserId() {
return currentUser.get();
}
public Facebook getFacebook() {
return facebook;
}
}
SocialContext实现Spring Social的ConnectionSignUp和SignInAdapter接口。 它包含三个方法isSignedIn() , signIn() , execute() 。 FacebookSignsController类调用isSignedIn来实现上述逻辑,而Spring Social调用signIn()和execute() 。
从我以前的博客中,您会记住,OAuth需要在浏览器,您的应用程序和SaaS提供程序之间进行多次旅行。 在进行这些操作时,应用程序需要保存多个OAuth参数的状态,例如:client_id,redirect_uri和其他参数。 通过将OAuth对话的状态映射到您的Webapp所控制的变量,Spring Social将所有这些复杂性从应用程序中隐藏起来。 这是userId ; 但是,不要以为它是用户名,因为它从未被用户看到,它只是一个唯一标识符,该标识符将许多HTTP请求链接到Spring Social核心中的SaaS提供程序连接(例如Facebook)。
由于其简单性,我遵循了Keith Donald的想法,即使用cookie在浏览器和服务器之间传递用户ID来保持状态。 我还从Spring Social快速入门中借用了他的UserCookieGenerator类来帮助我。
isSignedIn(...)方法使用UserCookieGenerator来确定HttpServletRequest对象是否包含包含有效用户ID的cookie。 如果这样做的话,它还会找出Spring Social的UsersConnectionRepository是否包含链接到相同用户ID的ConnectionRepository 。 如果这两个测试都返回true,则应用程序将请求并显示用户的Facebook数据。 如果两个测试之一返回false,则将要求用户登录。
SocialContext是专门为该示例编写的,并且包含足够的功能来演示我在此博客中所讨论的内容。 这意味着它目前有点粗糙并且可以使用,尽管可以对其进行改进以涵盖与任何/许多提供程序的连接,然后在不同的应用程序中重复使用。
最后要提到的类是FacebookConfig ,它大致基于Spring Social示例代码。 此代码与示例代码之间有两个主要区别,其中一个是FacebookConfig类实现InitializingBean接口。 这样,可以将usersConnectionRepositiory变量注入到socialContext中, 然后可以将socialContext作为其ConnectionSignUp实现注入到usersConnectionRepositiory中。 第二个区别是我正在实现providerSignInController(...)方法,以提供正确配置的ProviderSignInController对象,Spring Social将使用该对象登录Facebook。 我在此处所做的默认设置的唯一更改是将ProviderSignInController的postSignInUrl属性设置为“ / posts ”。 这是页面的URL,将包含用户Facebook数据,并在用户登录完成后被调用。
@Configuration
public class FacebookConfig implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(FacebookConfig.class);
private static final String appId = '439291719425239';
private static final String appSecret = '65646c3846ab46f0b44d73bb26087f06';
private SocialContext socialContext;
private UsersConnectionRepository usersConnectionRepositiory;
@Inject
private DataSource dataSource;
/**
* Point to note: the name of the bean is either the name of the method
* 'socialContext' or can be set by an attribute
*
* @Bean(name='myBean')
*/
@Bean
public SocialContext socialContext() {
return socialContext;
}
@Bean
public ConnectionFactoryLocator connectionFactoryLocator() {
logger.info('getting connectionFactoryLocator');
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory(appId, appSecret));
return registry;
}
/**
* Singleton data access object providing access to connections across all
* users.
*/
@Bean
public UsersConnectionRepository usersConnectionRepository() {
return usersConnectionRepositiory;
}
/**
* Request-scoped data access object providing access to the current user's
* connections.
*/
@Bean
@Scope(value = 'request', proxyMode = ScopedProxyMode.INTERFACES)
public ConnectionRepository connectionRepository() {
String userId = socialContext.getUserId();
logger.info('Createung ConnectionRepository for user: ' + userId);
return usersConnectionRepository().createConnectionRepository(userId);
}
/**
* A proxy to a request-scoped object representing the current user's
* primary Facebook account.
*
* @throws NotConnectedException
* if the user is not connected to facebook.
*/
@Bean
@Scope(value = 'request', proxyMode = ScopedProxyMode.INTERFACES)
public Facebook facebook() {
return connectionRepository().getPrimaryConnection(Facebook.class).getApi();
}
/**
* Create the ProviderSignInController that handles the OAuth2 stuff and
* tell it to redirect back to /posts once sign in has completed
*/
@Bean
public ProviderSignInController providerSignInController() {
ProviderSignInController providerSigninController = new ProviderSignInController(connectionFactoryLocator(),
usersConnectionRepository(), socialContext);
providerSigninController.setPostSignInUrl('/posts');
return providerSigninController;
}
@Override
public void afterPropertiesSet() throws Exception {
JdbcUsersConnectionRepository usersConnectionRepositiory = new JdbcUsersConnectionRepository(dataSource,
connectionFactoryLocator(), Encryptors.noOpText());
socialContext = new SocialContext(usersConnectionRepositiory, new UserCookieGenerator(), facebook());
usersConnectionRepositiory.setConnectionSignUp(socialContext);
this.usersConnectionRepositiory = usersConnectionRepositiory;
}
}
申请流程
如果您运行此应用程序2,首先会看到一个主屏幕,其中包含一个简单的链接,邀请您显示帖子。 首次单击此链接时,您将重定向到/ signin页面。 按下“登录”按钮,指示ProviderSignInController与Facebook联系。 身份验证完成后, ProviderSignInController会将应用程序定向回/ posts页面,这一次它将显示Facebook数据。
组态
为了完整起见,我认为我应该提到XML配置,尽管它没有太多,因为我在FacebookConfig类上使用了Spring注释@Configuration 。 我已经从Spring Social导入了“ data.xml ”,以便JdbcUsersConnectionRepository可以工作并添加了
<context:component-scan base-package='com.captaindebug.social' />
…用于自动接线。
摘要
尽管此示例应用程序基于将应用程序连接到用户的Facebook数据的基础,但可以轻松地对其进行修改以使用任何Spring Social客户端模块。 如果您喜欢挑战,请尝试在所有中文版本的地方实施Sina-Weibo-这是一个挑战,但是Google Translate确实很有用。
1个Spring社交和其他OAuth博客:
2该代码可在Github上找到:https://github.com/roghughe/captaindebug.git
参考: Captain Debug的Blog博客中的JCG合作伙伴 Roger Hughes的Spring Social入门-第2部分 。
翻译自: https://www.javacodegeeks.com/2012/07/getting-started-with-spring-social-part.html
spring social