在实现社交登录的时候我们通常会使用 spring Security Oauth2.0 和 spring Social 来完成授权登录.当时在集成中发现spring boot 1.5 和 2.0 在源码上有很大变动.
1.SocialAutoConfigurerAdapter 这个类已经在spring boot 2.x中被移除
若不想使用spring boot 1.5.x 可以通过继承 SocialConfigurerAdapter这个类
public class QQAutoConfig extends SocialConfigurerAdapter { @Autowired private SecurityProperties securityProperties; @Autowired private DataSource dataSource; private ConnectionFactoryRegistry connectionFactoryRegistry = new ConnectionFactoryRegistry(); @Override public void addConnectionFactories(ConnectionFactoryConfigurer configurer, Environment environment) { configurer.addConnectionFactory(this.createConnectionFactory()); } @Override public UserIdSource getUserIdSource() { return new AuthenticationNameUserIdSource(); } @Override public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { JdbcSocialUsersConnectionRepository repository = new JdbcSocialUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText()); //设置表的前缀和数据库一致 repository.setTablePrefix("xxx_"); return repository; } protected ConnectionFactory<?> createConnectionFactory() { QQProperties qqConfig = securityProperties.getSocial().getQq(); QQConnectionFactory qqConnectionFactory = new QQConnectionFactory(qqConfig.getProviderId(), qqConfig.getAppId(), qqConfig.getAppSecret()); connectionFactoryRegistry.addConnectionFactory(qqConnectionFactory); return qqConnectionFactory; }
这样就可以得到addConnectionFactories(), getUserIdSource(),getUsersConnectionRepository()这3个方法然后就可以去根据自己的需求实现方法
2.ConnectionFactoryLocator这个类无法注入
在使用ProviderSignInUtils对获取的社交平台的用户信息封装时,ProviderSignInUtils创建需要2个参数其中一个就是ConnectionFactoryLocator,
这个ConnectionFactoryLocator接口类型在spring boot 2.x中在spring容器里是没有实现类的,所以我们无法注入,我们可以同个new这个类的一个实现类来完成
例如: private ConnectionFactoryRegistry connectionFactoryRegistry = new ConnectionFactoryRegistry();
但是这样new出来的ConnectionFactoryRegistry是一个空对象,所以我们在创建ConnectionFactory的时候调用connectionFactoryRegistry.addConnectionFactory(ConnectionFactory connectionFactory )为connectionFactoryRegistry赋值;
例如:
protected ConnectionFactory<?> createConnectionFactory() { QQProperties qqConfig = securityProperties.getSocial().getQq(); QQConnectionFactory qqConnectionFactory = new QQConnectionFactory(qqConfig.getProviderId(), qqConfig.getAppId(), qqConfig.getAppSecret()); connectionFactoryRegistry.addConnectionFactory(qqConnectionFactory); return qqConnectionFactory; }
我们在使用ProviderSignInUtils配置@Bean的时候就可以这样使用了
例如
@Bean public ProviderSignInUtils providerSignInUtils() { return new ProviderSignInUtils(connectionFactoryRegistry, getUsersConnectionRepository(connectionFactoryRegistry)); }
3.在使用ProviderSignInUtils这个工具类来做社交账户登录查询和持久化存储的时候报错问题
通常我们会将第三方社交平台的用户和我们本地的用户做个关联,把第三方用户和本地用户关联关系做持久化存储.
使用social为我们提供的JdbcUsersConnectionRepository.sql可以快速创建表结构,
然后使用providerSignInUtils.doPostSignUp(userId, new ServletWebRequest(request));做持久化存储.
providerSignInUtils最终会调用JdbcConnectionRepository 的 addConnection(Connection<?> connection) 操作数据库.
@Transactional public void addConnection(Connection<?> connection) { try { ConnectionData data = connection.createData(); int rank = jdbcTemplate.queryForObject("select coalesce(max(rank) + 1, 1) as rank from " + tablePrefix + "UserConnection where userId = ? and providerId = ?", new Object[]{ userId, data.getProviderId() }, Integer.class); jdbcTemplate.update("insert into " + tablePrefix + "UserConnection (userId, providerId, providerUserId, rank, displayName, profileUrl, imageUrl, accessToken, secret, refreshToken, expireTime) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", userId, data.getProviderId(), data.getProviderUserId(), rank, data.getDisplayName(), data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()), encrypt(data.getSecret()), encrypt(data.getRefreshToken()), data.getExpireTime()); } catch (DuplicateKeyException e) { throw new DuplicateConnectionException(connection.getKey()); } }
期间会执行SELECT COALESCE(MAX(rank) + 1, 1) FROM UserConnection WHERE userId = 1 AND providerId = 1这条语句来查询rank,
但是我发现这条语句在mysql 8.0.15 版本中是执行失败的,经过过查证发现 rank为mysql关键字,所以,上面social提供的sql建表语句是有问题的,我们可以更改语句中的rank字段,例如:改成socialrank(没有要求,只要能执行成功就行),然后不在使用下面图片上的两个类
在本地新建这两个类将JdbcConnectionRepository 这个类中的 rank 替换成数据库中的字段(我是socialrank).然后
在初始化UsersConnectionRepository 时不再使用social提供的JdbcUsersConnectionRepository 改用自己改过的JdbcSocialUsersConnectionRepository即可
@Override public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { JdbcSocialUsersConnectionRepository repository = new JdbcSocialUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText()); //设置表的前缀和数据库一致 repository.setTablePrefix("xxx_"); return repository; }