【Spring Security OAuth2笔记系列】- Spring Social第三方登录 - 处理注册逻辑

处理注册逻辑

获取完用户信息,就跳转到了 http://mrcode.cn/signup ; 注册页面

为什么会跳转到注册页面呢?学习了这么长时间,核心基本原理也了解了,解决问题的方法也了解了,
那么先靠自己来尝试解决下,一步一步的跟着代码,发现报了一个错误

org.springframework.social.security.SocialAuthenticationProvider#authenticate

String userId = toUserId(connection);
  if (userId == null) {
    throw new BadCredentialsException("Unknown access token");
  }

  protected String toUserId(Connection<?> connection) {
        List<String> userIds = usersConnectionRepository.findUserIdsWithConnection(connection);
        // only if a single userId is connected to this providerUserId
        return (userIds.size() == 1) ? userIds.iterator().next() : null;
    }
看源码,这里使用了查询数据库,没有获取到userId,标识该用户还没有在我们的业务系统中绑定

设置默认跳转到注册页面

就是把默认的/signUp路径修改成我们自己的路径;

提供一个配置,支持使用方自定义注册页面

@Bean
public SpringSocialConfigurer imoocSocialSecurityConfig() {
    // 默认配置类,进行组件的组装
    // 包括了过滤器SocialAuthenticationFilter 添加到security过滤链中
    SpringSocialConfigurer springSocialConfigurer = new SpringSocialConfigurer();
    springSocialConfigurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
    return springSocialConfigurer;
}

怎么让注册动作与social互动?

  1. 怎么拿到用户授权后获取的用户信息?
  2. 怎么让注册这个动作与social互动(也就是要把关联信息插入到数据库中)

关键工具:org.springframework.social.connect.web.ProviderSignInUtils
目前不知道这个工具从哪里来的;

原理:原理就是把存储在session中的用户信息获取到;

org.springframework.social.security.SocialAuthenticationFilter#doAuthentication
private Authentication doAuthentication(SocialAuthenticationService<?> authService, HttpServletRequest request, SocialAuthenticationToken token) {
  try {
    if (!authService.getConnectionCardinality().isAuthenticatePossible()) return null;
    token.setDetails(authenticationDetailsSource.buildDetails(request));
    Authentication success = getAuthenticationManager().authenticate(token);
    Assert.isInstanceOf(SocialUserDetails.class, success.getPrincipal(), "unexpected principle type");
    updateConnections(authService, token, success);         
    return success;
  } catch (BadCredentialsException e) {
    // connection unknown, register new user?
    if (signupUrl != null) {
      // 跳转到注册页面前把连接信息存入session
      //
      sessionStrategy.setAttribute(new ServletWebRequest(request), ProviderSignInAttempt.SESSION_ATTRIBUTE, new ProviderSignInAttempt(token.getConnection()));
      throw new SocialAuthenticationRedirectException(buildSignupUrl(request));
    }
    throw e;
  }
}

实现:

  1. 提供获取用户信息的接口
  2. 提供注册方法,然后使用工具类交互插入数据库,再次登录的时候就不会再次跳往注册页面了

获取用户的接口:

cn.mrcode.imooc.springsecurity.securitybrowser.BrowserSecurityController
/**
 * see {@link SocialConfig#providerSignInUtils(org.springframework.social.connect.ConnectionFactoryLocator, org.springframework.social.connect.UsersConnectionRepository)}
 */
@Autowired
private ProviderSignInUtils providerSignInUtils;
@GetMapping("/social/user")
public SocialUserInfo getSocialUserInfo(javax.servlet.http.HttpServletRequest request) {
    SocialUserInfo userInfo = new SocialUserInfo();
    Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
    userInfo.setProviderId(connection.getKey().getProviderId());
    userInfo.setProviderUserId(connection.getKey().getProviderUserId());
    userInfo.setNickname(connection.getDisplayName());
    userInfo.setHeadimg(connection.getImageUrl());
    return userInfo;
}

提供注册方法:在demo里面,因为注册的逻辑是使用方才知道

com.example.demo.web.controller.UserController

@PostMapping("/regist")
public void regist(User user, HttpServletRequest request) {

    //不管是注册用户还是绑定用户,都会拿到一个用户唯一标识。
    String userId = user.getUsername();
    // 在这里就可以执行绑定或则注册用户的逻辑了
    // 然后使用 doPostSignUp 进行插入数据库
    providerSignInUtils.doPostSignUp(userId, new ServletWebRequest(request));
}

怎么让没有查询到userId的用户不跳转到注册页面,默认注册一个账户?

在这样的场景下;有一部分网站是使用qq登录就默认为你注册一个账户。然后直接完成登录;

原理是:

之前的源码中没有获取到 userId的地方 :
org.springframework.social.security.SocialAuthenticationProvider#toUserId
protected String toUserId(Connection<?> connection) {
  List<String> userIds = usersConnectionRepository.findUserIdsWithConnection(connection);
  // only if a single userId is connected to this providerUserId
  return (userIds.size() == 1) ? userIds.iterator().next() : null;
}

org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository#findUserIdsWithConnection

public List<String> findUserIdsWithConnection(Connection<?> connection) {
        ConnectionKey key = connection.getKey();
        List<String> localUserIds = jdbcTemplate.queryForList("select userId from " + tablePrefix + "UserConnection where providerId = ? and providerUserId = ?", String.class, key.getProviderId(), key.getProviderUserId());      
        if (localUserIds.size() == 0 && connectionSignUp != null) {
      // 注意这里,connectionSignUp 可以返回一个新的userid
      // 在这里就可以插入我们自己的逻辑,比如默认注册用户
            String newUserId = connectionSignUp.execute(connection);
            if (newUserId != null)
            {
                createConnectionRepository(newUserId).addConnection(connection);
                return Arrays.asList(newUserId);
            }
        }
        return localUserIds;
    }

实现:由于这样个性化的也属性使用方控制

package com.example.demo.security;

import cn.mrcode.imooc.springsecurity.securitycore.social.SocialConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Component;

/**
 * 第三方登录,默认注册用户
 * @author : zhuqiang
 * @version : V1.0
 * @date : 2018/8/6 20:04
 * @see SocialConfig#connectionSignUp  该对象存在则会在该地方被使用
 */
@Component
public class DemoConnectionSignUp implements ConnectionSignUp {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public String execute(Connection<?> connection) {
        logger.info("根据社交用户信息默认创建用户并返回用户唯一标识");
        return connection.getDisplayName();
    }
}

构建UsersConnectionRepository的时候把ConnectionSignUp实现设置进去

@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private DataSource dataSource;

    /**
     * 不存在则不使用默认注册用户,而是跳转到注册页完成注册或则绑定
     */
    @Autowired(required = false)
    private ConnectionSignUp connectionSignUp;

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
        repository.setTablePrefix("imooc_");
        repository.setConnectionSignUp(connectionSignUp);
        return repository;
    }

运行查看效果:直接默认插入了一条数据到数据库中完成了默认的注册;直接认证登录成功

INSERT INTO `imooc-demo`.`imooc_userconnection` (`userId`, `providerId`, `providerUserId`, `rank`, `displayName`, `profileUrl`, `imageUrl`, `accessToken`, `secret`, `refreshToken`, `expireTime`) VALUES ('猪', 'qq', '81F03E50B76D6D829F5A4875941567A6', '1', '猪', NULL, 'http://thirdqq.qlogo.cn/qqapp/101316278/81F03E50B76D6D829F5A4875941567A6/40', '2FCF43C2BA45ECD4CA4508FC8DC2CED8', NULL, 'D5C83B6F2E95DA9B4B97849113F15972', '1541333450678');
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值