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.