为MongoDB定制Spring Social Connect框架
在上一篇文章中 ,我谈到了我面临的第一个挑战是更改数据模型并添加连接框架。 在这里,我想提供有关我如何做的更多细节。 Spring Social项目已经提供了基于jdbc的连接存储库实现,以将用户连接数据持久保存到关系数据库中。 但是,我使用的是MongoDB,因此我需要自定义代码,并且发现这样做相对容易。 用户连接数据将保存为UserSocialConnection
的对象,它是一个MongoDB文档:
@SuppressWarnings('serial')
@Document(collection = 'UserSocialConnection')
public class UserSocialConnection extends BaseEntity {private String userId;private String providerId;private String providerUserId;private String displayName;private String profileUrl;private String imageUrl;private String accessToken;private String secret;private String refreshToken;private Long expireTime;//Getter/Setter omitted.public UserSocialConnection() {super();}public UserSocialConnection(String userId, String providerId, String providerUserId, int rank,String displayName, String profileUrl, String imageUrl, String accessToken, String secret,String refreshToken, Long expireTime) {super();this.userId = userId;this.providerId = providerId;this.providerUserId = providerUserId;this.displayName = displayName;this.profileUrl = profileUrl;this.imageUrl = imageUrl;this.accessToken = accessToken;this.secret = secret;this.refreshToken = refreshToken;this.expireTime = expireTime;}
}
BaseEntity
仅具有“ id”。 在Spring Data项目的帮助下,我不需要为UserSocialConnection
编写任何CRUD操作代码,只需扩展MongoRepository
:
public interface UserSocialConnectionRepository extends MongoRepository<UserSocialConnection, String>{List<UserSocialConnection> findByUserId(String userId);List<UserSocialConnection> findByUserIdAndProviderId(String userId, String providerId);List<UserSocialConnection> findByProviderIdAndProviderUserId(String providerId, String providerUserId);UserSocialConnection findByUserIdAndProviderIdAndProviderUserId(String userId, String providerId, String providerUserId);List<UserSocialConnection> findByProviderIdAndProviderUserIdIn(String providerId, Collection<String> providerUserIds);
}
在拥有数据库UserSocialConnectionRepository
,我们将实现Spring Social所需的ConnectionRepository
和UsersConnectionRepository
。 我只是从JdbcConnectionRepository
和JdbcUsersConnectionRepository
复制了代码,并创建了自己的MongoConnectionRepository
和MongoUsersConnectionRepository
。
public class MongoUsersConnectionRepository implements UsersConnectionRepository{private final UserSocialConnectionRepository userSocialConnectionRepository;private final SocialAuthenticationServiceLocator socialAuthenticationServiceLocator;private final TextEncryptor textEncryptor;private ConnectionSignUp connectionSignUp;public MongoUsersConnectionRepository(UserSocialConnectionRepository userSocialConnectionRepository, SocialAuthenticationServiceLocator socialAuthenticationServiceLocator, TextEncryptor textEncryptor){this.userSocialConnectionRepository = userSocialConnectionRepository;this.socialAuthenticationServiceLocator = socialAuthenticationServiceLocator;this.textEncryptor = textEncryptor;}/*** The command to execute to create a new local user profile in the event no user id could be mapped to a connection.* Allows for implicitly creating a user profile from connection data during a provider sign-in attempt.* Defaults to null, indicating explicit sign-up will be required to complete the provider sign-in attempt.* @see #findUserIdsWithConnection(Connection)*/public void setConnectionSignUp(ConnectionSignUp connectionSignUp) {this.connectionSignUp = connectionSignUp;}public List<String> findUserIdsWithConnection(Connection<?> connection) {ConnectionKey key = connection.getKey();List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByProviderIdAndProviderUserId(key.getProviderId(), key.getProviderUserId());List<String> localUserIds = new ArrayList<String>();for (UserSocialConnection userSocialConnection : userSocialConnectionList){localUserIds.add(userSocialConnection.getUserId());}if (localUserIds.size() == 0 && connectionSignUp != null) {String newUserId = connectionSignUp.execute(connection);if (newUserId != null){createConnectionRepository(newUserId).addConnection(connection);return Arrays.asList(newUserId);}}return localUserIds;}public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {final Set<String> localUserIds = new HashSet<String>();List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByProviderIdAndProviderUserIdIn(providerId, providerUserIds);for (UserSocialConnection userSocialConnection : userSocialConnectionList){localUserIds.add(userSocialConnection.getUserId());}return localUserIds;}public ConnectionRepository createConnectionRepository(String userId) {if (userId == null) {throw new IllegalArgumentException('userId cannot be null');}return new MongoConnectionRepository(userId, userSocialConnectionRepository, socialAuthenticationServiceLocator, textEncryptor);}}
MongoUsersConnectionRepository
非常类似于JdbcUsersConnectionRepository
。 但是对于MongoConnectionRepository
,我需要进行一些更改:
public class MongoConnectionRepository implements ConnectionRepository {private final String userId;private final UserSocialConnectionRepository userSocialConnectionRepository;private final SocialAuthenticationServiceLocator socialAuthenticationServiceLocator;private final TextEncryptor textEncryptor;public MongoConnectionRepository(String userId, UserSocialConnectionRepository userSocialConnectionRepository,SocialAuthenticationServiceLocator socialAuthenticationServiceLocator, TextEncryptor textEncryptor) {this.userId = userId;this.userSocialConnectionRepository = userSocialConnectionRepository;this.socialAuthenticationServiceLocator = socialAuthenticationServiceLocator;this.textEncryptor = textEncryptor;}public MultiValueMap<String, Connection<?>> findAllConnections() {List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByUserId(userId);MultiValueMap<String, Connection<?>> connections = new LinkedMultiValueMap<String, Connection<?>>();Set<String> registeredProviderIds = socialAuthenticationServiceLocator.registeredProviderIds();for (String registeredProviderId : registeredProviderIds) {connections.put(registeredProviderId, Collections.<Connection<?>> emptyList());}for (UserSocialConnection userSocialConnection : userSocialConnectionList) {String providerId = userSocialConnection.getProviderId();if (connections.get(providerId).size() == 0) {connections.put(providerId, new LinkedList<Connection<?>>());}connections.add(providerId, buildConnection(userSocialConnection));}return connections;}public List<Connection<?>> findConnections(String providerId) {List<Connection<?>> resultList = new LinkedList<Connection<?>>();List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByUserIdAndProviderId(userId, providerId);for (UserSocialConnection userSocialConnection : userSocialConnectionList) {resultList.add(buildConnection(userSocialConnection));}return resultList;}@SuppressWarnings('unchecked')public <A> List<Connection<A>> findConnections(Class<A> apiType) {List<?> connections = findConnections(getProviderId(apiType));return (List<Connection<A>>) connections;}public MultiValueMap<String, Connection<?>> findConnectionsToUsers(MultiValueMap<String, String> providerUsers) {if (providerUsers == null || providerUsers.isEmpty()) {throw new IllegalArgumentException('Unable to execute find: no providerUsers provided');}MultiValueMap<String, Connection<?>> connectionsForUsers = new LinkedMultiValueMap<String, Connection<?>>();for (Iterator<Entry<String, List<String>>> it = providerUsers.entrySet().iterator(); it.hasNext();) {Entry<String, List<String>> entry = it.next();String providerId = entry.getKey();List<String> providerUserIds = entry.getValue();List<UserSocialConnection> userSocialConnections = this.userSocialConnectionRepository.findByProviderIdAndProviderUserIdIn(providerId, providerUserIds);List<Connection<?>> connections = new ArrayList<Connection<?>>(providerUserIds.size());for (int i = 0; i < providerUserIds.size(); i++) {connections.add(null);}connectionsForUsers.put(providerId, connections);for (UserSocialConnection userSocialConnection : userSocialConnections) {String providerUserId = userSocialConnection.getProviderUserId();int connectionIndex = providerUserIds.indexOf(providerUserId);connections.set(connectionIndex, buildConnection(userSocialConnection));}}return connectionsForUsers;}public Connection<?> getConnection(ConnectionKey connectionKey) {UserSocialConnection userSocialConnection = this.userSocialConnectionRepository.findByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(),connectionKey.getProviderUserId());if (userSocialConnection != null) {return buildConnection(userSocialConnection);}throw new NoSuchConnectionException(connectionKey);}@SuppressWarnings('unchecked')public <A> Connection<A> getConnection(Class<A> apiType, String providerUserId) {String providerId = getProviderId(apiType);return (Connection<A>) getConnection(new ConnectionKey(providerId, providerUserId));}@SuppressWarnings('unchecked')public <A> Connection<A> getPrimaryConnection(Class<A> apiType) {String providerId = getProviderId(apiType);Connection<A> connection = (Connection<A>) findPrimaryConnection(providerId);if (connection == null) {throw new NotConnectedException(providerId);}return connection;}@SuppressWarnings('unchecked')public <A> Connection<A> findPrimaryConnection(Class<A> apiType) {String providerId = getProviderId(apiType);return (Connection<A>) findPrimaryConnection(providerId);}public void addConnection(Connection<?> connection) {//check cardinalitySocialAuthenticationService<?> socialAuthenticationService = this.socialAuthenticationServiceLocator.getAuthenticationService(connection.getKey().getProviderId());if (socialAuthenticationService.getConnectionCardinality() == ConnectionCardinality.ONE_TO_ONE ||socialAuthenticationService.getConnectionCardinality() == ConnectionCardinality.ONE_TO_MANY){List<UserSocialConnection> storedConnections = this.userSocialConnectionRepository.findByProviderIdAndProviderUserId(connection.getKey().getProviderId(), connection.getKey().getProviderUserId());if (storedConnections.size() > 0){//not allow one providerId connect to multiple userIdthrow new DuplicateConnectionException(connection.getKey());}}UserSocialConnection userSocialConnection = this.userSocialConnectionRepository.findByUserIdAndProviderIdAndProviderUserId(userId, connection.getKey().getProviderId(), connection.getKey().getProviderUserId());if (userSocialConnection == null) {ConnectionData data = connection.createData();userSocialConnection = new UserSocialConnection(userId, data.getProviderId(), data.getProviderUserId(), 0,data.getDisplayName(), data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()),encrypt(data.getSecret()), encrypt(data.getRefreshToken()), data.getExpireTime());this.userSocialConnectionRepository.save(userSocialConnection);} else {throw new DuplicateConnectionException(connection.getKey());}}public void updateConnection(Connection<?> connection) {ConnectionData data = connection.createData();UserSocialConnection userSocialConnection = this.userSocialConnectionRepository.findByUserIdAndProviderIdAndProviderUserId(userId, connection.getKey().getProviderId(), connection.getKey().getProviderUserId());if (userSocialConnection != null) {userSocialConnection.setDisplayName(data.getDisplayName());userSocialConnection.setProfileUrl(data.getProfileUrl());userSocialConnection.setImageUrl(data.getImageUrl());userSocialConnection.setAccessToken(encrypt(data.getAccessToken()));userSocialConnection.setSecret(encrypt(data.getSecret()));userSocialConnection.setRefreshToken(encrypt(data.getRefreshToken()));userSocialConnection.setExpireTime(data.getExpireTime());this.userSocialConnectionRepository.save(userSocialConnection);}}public void removeConnections(String providerId) {List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByUserIdAndProviderId(userId, providerId);for (UserSocialConnection userSocialConnection : userSocialConnectionList) {this.userSocialConnectionRepository.delete(userSocialConnection);}}public void removeConnection(ConnectionKey connectionKey) {UserSocialConnection userSocialConnection = this.userSocialConnectionRepository.findByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(), connectionKey.getProviderUserId());this.userSocialConnectionRepository.delete(userSocialConnection);}// internal helpersprivate Connection<?> buildConnection(UserSocialConnection userSocialConnection) {ConnectionData connectionData = new ConnectionData(userSocialConnection.getProviderId(),userSocialConnection.getProviderUserId(), userSocialConnection.getDisplayName(),userSocialConnection.getProfileUrl(), userSocialConnection.getImageUrl(),decrypt(userSocialConnection.getAccessToken()), decrypt(userSocialConnection.getSecret()),decrypt(userSocialConnection.getRefreshToken()), userSocialConnection.getExpireTime());ConnectionFactory<?> connectionFactory = this.socialAuthenticationServiceLocator.getConnectionFactory(connectionData.getProviderId());return connectionFactory.createConnection(connectionData);}private Connection<?> findPrimaryConnection(String providerId) {List<UserSocialConnection> userSocialConnectionList = this.userSocialConnectionRepository.findByUserIdAndProviderId(userId, providerId);return buildConnection(userSocialConnectionList.get(0));}private <A> String getProviderId(Class<A> apiType) {return socialAuthenticationServiceLocator.getConnectionFactory(apiType).getProviderId();}private String encrypt(String text) {return text != null ? textEncryptor.encrypt(text) : text;}private String decrypt(String encryptedText) {return encryptedText != null ? textEncryptor.decrypt(encryptedText) : encryptedText;}}
首先,我将JdbcTemplate
替换为UserSocialConnectionRepository
以从数据库中检索UserSocialConnection对象。 然后用spring-social-security模块中的SocialAuthenticationServiceLocator
替换ConnectionFactoryLocator
。 最大的变化是addConnection
方法(上面已突出显示),它首先检查连接基数。 如果connectionCardinality
的socialAuthenticationService
是ONE_TO_ONE
(这意味着一个用户id与一个且仅一个对providerId / providerUserId的),或ONE_TO_MANY
(这意味着一个用户id可以连接到一个或多个providerId / providerUserId,但一对providerId / providerUserId的只能连接到一个userId)。
完成所有这些自定义之后,最后一步是在spring config中将它们粘合在一起:
@Configuration
public class SocialAndSecurityConfig {@Injectprivate Environment environment;@InjectAccountService accountService;@Injectprivate AuthenticationManager authenticationManager;@Injectprivate UserSocialConnectionRepository userSocialConnectionRepository;@Beanpublic SocialAuthenticationServiceLocator socialAuthenticationServiceLocator() {SocialAuthenticationServiceRegistry registry = new SocialAuthenticationServiceRegistry();//add googleOAuth2ConnectionFactory<Google> googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty('google.clientId'),environment.getProperty('google.clientSecret'));OAuth2AuthenticationService<Google> googleAuthenticationService = new OAuth2AuthenticationService<Google>(googleConnectionFactory);googleAuthenticationService.setScope('https://www.googleapis.com/auth/userinfo.profile');registry.addAuthenticationService(googleAuthenticationService);//add twitterOAuth1ConnectionFactory<Twitter> twitterConnectionFactory = new TwitterConnectionFactory(environment.getProperty('twitter.consumerKey'),environment.getProperty('twitter.consumerSecret'));OAuth1AuthenticationService<Twitter> twitterAuthenticationService = new OAuth1AuthenticationService<Twitter>(twitterConnectionFactory);registry.addAuthenticationService(twitterAuthenticationService);//add facebookOAuth2ConnectionFactory<Facebook> facebookConnectionFactory = new FacebookConnectionFactory(environment.getProperty('facebook.clientId'),environment.getProperty('facebook.clientSecret'));OAuth2AuthenticationService<Facebook> facebookAuthenticationService = new OAuth2AuthenticationService<Facebook>(facebookConnectionFactory);facebookAuthenticationService.setScope('');registry.addAuthenticationService(facebookAuthenticationService);return registry;}/*** Singleton data access object providing access to connections across all users.*/@Beanpublic UsersConnectionRepository usersConnectionRepository() {MongoUsersConnectionRepository repository = new MongoUsersConnectionRepository(userSocialConnectionRepository,socialAuthenticationServiceLocator(), Encryptors.noOpText());repository.setConnectionSignUp(autoConnectionSignUp());return repository;}/*** Request-scoped data access object providing access to the current user's connections.*/@Bean@Scope(value = 'request', proxyMode = ScopedProxyMode.INTERFACES)public ConnectionRepository connectionRepository() {UserAccount user = AccountUtils.getLoginUserAccount();return usersConnectionRepository().createConnectionRepository(user.getUsername());}/*** A proxy to a request-scoped object representing the current user's primary Google account.* * @throws NotConnectedException* if the user is not connected to Google.*/@Bean@Scope(value = 'request', proxyMode = ScopedProxyMode.INTERFACES)public Google google() {Connection<Google> google = connectionRepository().findPrimaryConnection(Google.class);return google != null ? google.getApi() : new GoogleTemplate();}@Bean@Scope(value='request', proxyMode=ScopedProxyMode.INTERFACES) public Facebook facebook() {Connection<Facebook> facebook = connectionRepository().findPrimaryConnection(Facebook.class);return facebook != null ? facebook.getApi() : new FacebookTemplate();}@Bean@Scope(value='request', proxyMode=ScopedProxyMode.INTERFACES) public Twitter twitter() {Connection<Twitter> twitter = connectionRepository().findPrimaryConnection(Twitter.class);return twitter != null ? twitter.getApi() : new TwitterTemplate();}@Beanpublic ConnectionSignUp autoConnectionSignUp() {return new AutoConnectionSignUp(accountService);}@Beanpublic SocialAuthenticationFilter socialAuthenticationFilter() {SocialAuthenticationFilter filter = new SocialAuthenticationFilter(authenticationManager, accountService,usersConnectionRepository(), socialAuthenticationServiceLocator());filter.setFilterProcessesUrl('/signin');filter.setSignupUrl(null); filter.setConnectionAddedRedirectUrl('/myAccount');filter.setPostLoginUrl('/myAccount');return filter;}@Beanpublic SocialAuthenticationProvider socialAuthenticationProvider(){return new SocialAuthenticationProvider(usersConnectionRepository(), accountService);}@Beanpublic LoginUrlAuthenticationEntryPoint socialAuthenticationEntryPoint(){return new LoginUrlAuthenticationEntryPoint('/signin');}}
accountService
是我自己的用户帐户服务,用于提供与帐户相关的功能,它实现了SocialUserDetailsService
, UserDetailsService
, UserIdExtractor
。
还有很多地方需要改进,例如重构MongoConnectionRepository
和MongoUsersConnectionRepository
以使用Spring Data Repository接口实现抽象的社交连接存储库实现。 而且我发现有人已经对此提出了一个问题: 利用Spring Data for UsersConnectionRepository 。
参考:来自我们的JCG合作伙伴 Yuan Ji在Jiwhiz博客上为MongoDB定制Spring Social Connect Framework 。
翻译自: https://www.javacodegeeks.com/2013/03/customize-spring-social-connect-framework-for-mongodb.html
为MongoDB定制Spring Social Connect框架相关推荐
- 自定义MongoDB的Spring Social Connect框架
在上一篇文章中 ,我谈到了我面临的第一个挑战是更改数据模型并添加连接框架. 在这里,我想提供有关我如何做的更多详细信息. Spring Social项目已经提供了基于jdbc的连接存储库实现,以将用户 ...
- Spring Security技术栈学习笔记(十三)Spring Social集成第三方登录验证开发流程介绍
开发第三方登录,我们必须首先要了解OAuth协议(本文所讲述的OAuth协议指的是OAuth2协议),本文首先简单介绍OAuth协议,然后基于Spring Social来阐述开发第三方登录需要做哪些准 ...
- 进击的 Spring Cloud Alibaba —— 框架与服务
作者 | 陈曦(良名) Spring Cloud Alibaba 项目成员,start.aliyun.com 负责人. 导读:本文整理自作者于 2020 年云原生微服务大会上的分享<进击的 S ...
- Spring MVC测试框架入门–第2部分
这个迷你系列的第一个博客介绍了Spring MVC测试框架,并展示了其在单元测试Spring MVC Controller类中作为控制器而不是POJO进行单元测试的用途. 现在是时候讨论使用框架进行集 ...
- 14.6 Spring MVC 测试框架(翻译)
14.6 Spring MVC 测试框架(每天翻译一点点) Spring MVC测试框架对 Spring MVC 代码提供一流的测试支持 ,它拥有一个 fluent API ,可以和JUnit, Te ...
- Spring Security源码分析四:Spring Social实现微信社交登录
2019独角兽企业重金招聘Python工程师标准>>> 社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯 ...
- Spring Security系列之Spring Social实现微信社交登录(九)
社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...
- Spring Security技术栈学习笔记(十四)使用Spring Social集成QQ登录验证方式
上一篇文章<Spring Security技术栈开发企业级认证与授权(十三)Spring Social集成第三方登录验证开发流程介绍>主要是介绍了OAuth2协议的基本内容以及Spring ...
- spring struts2 mybatis框架学习总结(mvc三层架构)
spring struts2 mybatis框架学习总结(mvc三层架构) ssi的框架主要是由struts2,spring以及ibatis组成,他们负责各层之间的交互与协作,从而实现整个web端的功 ...
最新文章
- SAP MM MIGO 551 可以直接报废供应商寄售库存
- VS2005中ajax安装指南[转]
- Lesson 03:运算符与流程控制
- 【Python学习】 - 使用Anaconda的Spyder查看某些函数的原型的6种方法汇总
- (需求实战_进阶_03)SSM集成RabbitMQ 路由模式关键代码讲解、开发、测试
- 金九银十正确打开方式!快手三面面试真题
- JSP页面中taglib的uri设置
- QTTabBar功能是灰色,无法启用的解决办法
- 正则表达式三 不捕获文本 前瞻后顾 否定前瞻 否定后顾 贪婪匹配 懒惰匹配
- 计算机组成与体系结构——计算机体系结构分类-Flynn——2020.11.19
- 【目录】全志V3S学习记录
- input框动态模糊查询,能输入,能选择
- 串列配置(Tandem)在Kintex-7互联TRD中的实现
- 艾宾浩斯记忆遗忘曲线
- OpenCv-C++-亚像素级别角点检测(检测子像素中的corner的位置)
- J2SE-Java基础
- 750ti显卡能支持服务器吗,这个电脑配置能用GTX750Ti显卡吗
- Ubuntu+ visual studio + visualGdb
- 深度学习PyTorch,TensorFlow中GPU利用率较低,CPU利用率很低,且模型训练速度很慢的问题总结与分析
- 英语语法---比较级和最高级的用法
热门文章
- php 查看spl,PHP使用标准库spl实现的观察者模式示例
- java 时分秒格式小时8_Java里得到00:00:00格式的时分秒的Timestamp
- java oauth2.0_OAuth 2.0 Java指南:5分钟保护您的应用程序安全
- jdk 8 时区 转换_JDK 8 BigInteger精确缩小转换方法
- netbeans调试_从NetBeans运行和调试WildFly Swarm应用程序
- apache camel_使用Apache Camel进行负载平衡
- spring javaee_JavaEE还是Spring? 都不行! 我们呼吁新的竞争者!
- 您如何使用硒来计算自动化测试的投资回报率?
- 知识图谱 图数据库 推理_图数据库的知识表示与推理
- db2分页sql_停止尝试使用内部DB框架模拟SQL OFFSET分页!