[Spring Security + OAuth2 + JWT start to practice] 15. User registration after third-party QQ login

Posted by v00d00 on Mon, 09 Mar 2020 10:07:29 +0100

brief introduction

In the previous article, QQ login is completed. Generally, if the current QQ login user is not registered in our database, he should skip to the registration binding page. If we do not configure the jump page, he will jump to / signup by default

Source code analysis

Enter social authentication filter

    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) {
            //Determine whether the default registration page / signup is set
            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;
            }
        }
    }

Custom registration page

demo project first build registration page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Standard registration page</title>
</head>
<body>
<h2>demo Registration page</h2>
<h3>register</h3>
<!-- Both registration and binding are submitted to regist -->
<form action="/user/regist" method="post">
    <table>
        <tr>
            <td>User name:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2">
                <button type="submit" value="regist">binding</button>
                <button type="submit" value="binding">register</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

Modify the path of BrowserProperties class plus registration page

Note to add the following path to the configuration without authentication

   /**
     * Registration page
     */
    private String signUpUrl = "/signUp.html";

Modify the default registration page

Modify the SocialConfig class

    @Bean
    public SpringSocialConfigurer hkSocialSecurityConfig(){
        // Default configuration class for component assembly
        // Include filter SocialAuthenticationFilter added to security filter chain
        //Custom login path
        String filterProcessesUrl = this.securityProperties.getSocil().getFilterProcessesUrl();
        HkSpringSocialConfigurer configurer = new HkSpringSocialConfigurer(filterProcessesUrl);
        //Custom registration page
        configurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
        return configurer;
    }

Control layer Controller

Write the parameter of User entity class to receive the page first

package com.spring.security.dto;

import lombok.Getter;
import lombok.Setter;

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

    //User name
    private String username;

    //Password
    private String password;

}

There are two problems in doing / user / register:

  • How to retrieve the data of QQ logged in users from spring social
  • How to give the data configuration to spring social to add to the database when the user clicks the binding

Solve the problem of getting the data of QQ login users from spring social

Spring social helps us provide help package ProviderSignInUtils

The SocialConfig class overrides the providerSignInUtils method

    //Access to user data
    @Bean
    public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator) {
        return new ProviderSignInUtils(connectionFactoryLocator, getUsersConnectionRepository(connectionFactoryLocator));
    }

BrowserSecurityController class add external access interface

Create return entity class SocialUserInfo class

package com.spring.security.support;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class SocialUsrInfo {
    //Application of id
    private String providerId;

    //User Id
    private String providerUserId;

    //User name
    private String nickname;

    //User head
    private String headimg;
}

BrowserSecurityController class adding method

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    /**
     * Read user springSocial data
     * @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()));

In this step, the interceptor puts the user's information into the session

After QQ login, you can access the user information in spring social through / social/user

How to give the data configuration to spring social to add to the database when the user clicks the binding

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 "Login successfully jump";
    }


    @GetMapping("failure")
    public String failure() {
        return "Login failed jump";
    }

    @Autowired
    private ProviderSignInUtils providerSignInUtils;

    /**
     * Registration and binding
     *
     * @return
     */
    @PostMapping("/user/regist")
    public void regist(User user, HttpServletRequest request) {
        //No matter binding or adding, we will return the unique user ID tag through query or adding
        //I use the user name as the unique identification first
        String userId = user.getUsername();

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


}

Look at source code

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();
        // Query whether the current third-party data exists in the database, and throw exceptions if not
        // Query through JdbcUsersConnectionRepository
        String userId = toUserId(connection);
        if (userId == null) {
            throw new BadCredentialsException("Unknown access token");
        }
        //If the id is not empty, query the user information in the database
        UserDetails userDetails = userDetailsService.loadUserByUserId(userId);
        if (userDetails == null) {
            throw new UsernameNotFoundException("Unknown connected account id");
        }
        //And then put the queried information into social
        return new SocialAuthenticationToken(connection, userDetails, authToken.getProviderAccountData(), getAuthorities(providerId, userDetails));
    }

Note to add the path to the configuration that does not require authentication: / user / register

My QQ Mutual Aid Association will not be tested in the audit, please test by yourself.

Topics: Programming Spring Database Lombok Session