【Spring Security + OAuth2 + JWT入门到实战】15. 第三方QQ登录以后用户注册

让人想犯罪 __ 提交于 2020-03-09 16:37:20

简介

上一篇文章完成QQ登录,一般操作如果当前QQ登录用户在我们数据库没有注册那应该跳注册绑定页面,如果我们未配置跳转的页面默认会跳转到:/signup

源码分析

进入SocialAuthenticationFilter中

    private Authentication doAuthentication(SocialAuthenticationService<?> authService, HttpServletRequest request, SocialAuthenticationToken token) {
        try {
            if (!authService.getConnectionCardinality().isAuthenticatePossible()) {
                return null;
            } else {
                token.setDetails(this.authenticationDetailsSource.buildDetails(request));
                Authentication success = this.getAuthenticationManager().authenticate(token);
                Assert.isInstanceOf(SocialUserDetails.class, success.getPrincipal(), "unexpected principle type");
                this.updateConnections(authService, token, success);
                return success;
            }
        } catch (BadCredentialsException var5) {
            //判断是否设置了注册页面  默认注册页面/signup
            if (this.signupUrl != null) {
                this.sessionStrategy.setAttribute(new ServletWebRequest(request), ProviderSignInAttempt.SESSION_ATTRIBUTE, new ProviderSignInAttempt(token.getConnection()));
                throw new SocialAuthenticationRedirectException(this.buildSignupUrl(request));
            } else {
                throw var5;
            }
        }
    }

自定义注册页

demo项目先建注册页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标准注册页面</title>
</head>
<body>
<h2>demo注册页面</h2>
<h3>注册</h3>
<!-- 不管是注册还是绑定都提交到regist -->
<form action="/user/regist" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2">
                <button type="submit" value="regist">绑定</button>
                <button type="submit" value="binding">注册</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

修改BrowserProperties类加注册页面的路径

注意要把下面路径加到不需要认证配置

   /**
     * 注册页面
     */
    private String signUpUrl = "/signUp.html";

修改默认的注册页

修改SocialConfig类

    @Bean
    public SpringSocialConfigurer hkSocialSecurityConfig(){
        // 默认配置类,进行组件的组装
        // 包括了过滤器SocialAuthenticationFilter 添加到security过滤链中
        //自定义登录路径
        String filterProcessesUrl = this.securityProperties.getSocil().getFilterProcessesUrl();
        HkSpringSocialConfigurer configurer = new HkSpringSocialConfigurer(filterProcessesUrl);
        //自定义注册页
        configurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
        return configurer;
    }

控制层Controller

先写User实体类接收页面传的参数

package com.spring.security.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {
    //用户ID
    private String id;

    //用户名
    private String username;

    //密码
    private String password;

}

做/user/regist遇到两个问题:

  • 如何从springsocial里取出已QQ登录用户的资料
  • 如何在用户点击绑定的时候把数据配置好交给springsocial去添加到数据库

解决从springsocial里取出已QQ登录用户的资料

springsocial帮我们提供了帮助包ProviderSignInUtils

SocialConfig类重写providerSignInUtils方法

    //取用户资料
    @Bean
    public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator) {
        return new ProviderSignInUtils(connectionFactoryLocator, getUsersConnectionRepository(connectionFactoryLocator));
    }

BrowserSecurityController类添加对外访问的接口

创建返回实体类SocialUserInfo类

package com.spring.security.support;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class SocialUsrInfo {
    //应用id
    private String providerId;

    //用户Id
    private String providerUserId;

    //用户名称
    private String nickname;

    //用户头像
    private String headimg;
}

BrowserSecurityController类添加方法

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    /**
     * 读取用户springSocial资料
     * @return
     */
    @GetMapping("/social/user")
    public SocialUsrInfo getSocialUsrInfo(HttpServletRequest request){
        SocialUsrInfo socialUsrInfo = new SocialUsrInfo();
        Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
        socialUsrInfo.setProviderId(connection.getKey().getProviderId());
        socialUsrInfo.setProviderUserId(connection.getKey().getProviderUserId());
        socialUsrInfo.setNickname(connection.getDisplayName());
        socialUsrInfo.setHeadimg(connection.getImageUrl());
        return socialUsrInfo;
    }
this.sessionStrategy.setAttribute(new ServletWebRequest(request), ProviderSignInAttempt.SESSION_ATTRIBUTE, new ProviderSignInAttempt(token.getConnection()));

拦截器在这一步把用户的信息放到session里面

QQ登录成功以后就可以通过/social/user访问springsocial里面的用户资料

如何在用户点击绑定的时候把数据配置好交给springsocial去添加到数据库

package com.spring.security.controller;


import com.spring.security.dto.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;

@RestController
public class HelloController {

    @GetMapping("hello")
    public String hello() {
        return "hello spring security";
    }

    @GetMapping("index")
    public String index() {
        return "登录成功跳转";
    }


    @GetMapping("failure")
    public String failure() {
        return "登录失败跳转";
    }

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    /**
     * 注册和绑定
     *
     * @return
     */
    @PostMapping("/user/regist")
    public void regist(User user, HttpServletRequest request) {
        //这里不管是绑定还是添加  我们都会通过查询或者添加返回用户ID 唯一标
        //我先用用户名当成唯一标识
        String userId = user.getUsername();

        providerSignInUtils.doPostSignUp(userId, new ServletWebRequest(request));
    }


}

看源码

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Assert.isInstanceOf(SocialAuthenticationToken.class, authentication, "unsupported authentication type");
        Assert.isTrue(!authentication.isAuthenticated(), "already authenticated");
        SocialAuthenticationToken authToken = (SocialAuthenticationToken) authentication;
        String providerId = authToken.getProviderId();
        Connection<?> connection = authToken.getConnection();
        // 查询数据库是否存在当前第三方数据,不存在就抛异常
        // 通过JdbcUsersConnectionRepository查询
        String userId = toUserId(connection);
        if (userId == null) {
            throw new BadCredentialsException("Unknown access token");
        }
        //如果id不为空则去数据库查询用户信息
        UserDetails userDetails = userDetailsService.loadUserByUserId(userId);
        if (userDetails == null) {
            throw new UsernameNotFoundException("Unknown connected account id");
        }
        //然后把查询到的信息放到social
        return new SocialAuthenticationToken(connection, userDetails, authToken.getProviderAccountData(), getAuthorities(providerId, userDetails));
    }

注意要把路径加到不需要认证配置:/user/regist

我的QQ互助联在审核就不测试了,请自行测试。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!