spring boot 2.x 和 Security Oauth2.0 Social 实现社交帐号登录的问题

在实现社交登录的时候我们通常会使用 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;
}

转载于:https://my.oschina.net/u/2473610/blog/3023288

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值