In the previous section, we implemented a JSON login with a username and password plus a verification code. In this section, the requirements continue to escalate.
Requirements describe
This interface is required to implement:
- User name password + verification code login
- Mobile phone number + verification code login
- Third Party Id login
The solution
In this section, we’ll use the custom authentication token class mentioned in the previous chapter.
The entrance of the certification class (MyAuthenticationProcessingFilter) resolve the login parameters (LoginData), and then packing for authentication token class (MyAuthenticationToken), continue to pass down. Reach certification provides class (MyAuthenticationTokenProvider), the decision to land way, and then call the different method to land certification.
Also, we need to extend the login parameter class. In the last section, we only had three parameters: username, password, and commonLoginVerifyCode.
According to the demand, we need to expand to:
@Data
public class LoginData {
/** Login method */
private String loginType;
/** User name */
private String username;
/ * * * / passwords
private String password;
/** Common login verification code */
private String commonLoginVerifyCode;
/** Mobile phone number */
private String phone;
/** Mobile verification code */
private String phoneVerifyCode;
/** Third-party platform type */
private String thirdPlatformType;
/** Third-party platform ID */
private String thirdPlatformId;
}
Copy the code
implementation
The irrelevant logic is simplified.
-
Custom authentication classes
Imitate UsernamePasswordAuthenticationToken writing.
@Getter @Setter @ToString public class MyAuthenticationToken extends AbstractAuthenticationToken { private final Object principal; private Object credentials; private LoginData loginData; public MyAuthenticationToken(Object principal, Object credentials) { super(null); this.principal = principal; this.credentials = credentials; setAuthenticated(false); } public MyAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = credentials; // must use super, as we override super.setAuthenticated(true); } @Override public Object getCredentials(a) { return this.credentials; } @Override public Object getPrincipal(a) { return this.principal; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { if (isAuthenticated) { throw new IllegalArgumentException( "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); } super.setAuthenticated(false); } @Override public void eraseCredentials(a) { super.eraseCredentials(); credentials = null; } Copy the code
-
A custom Provider
Imitate DaoAuthenticationProvider. Write.
@Component @Slf4j public class MyAuthenticationTokenProvider extends AbstractUserDetailsAuthenticationProvider { @Autowired MyUserDetailServiceImpl myUserDetailService; @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {} // Authentication logic @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { MyAuthenticationToken myAuthenticationToken = (MyAuthenticationToken) authentication; log.info("{}",myAuthenticationToken.toString()); LoginData loginData = myAuthenticationToken.getLoginData(); if (loginData == null) { throw new AuthenticationServiceException("Login parameters not obtained"); } String loginType = loginData.getLoginType(); if (loginType == null || loginType.equals("")) { throw new AuthenticationServiceException("Login mode cannot be empty."); } UserDetails userDetails; if (LoginType.USERNAME_CODE.equals(loginType)) { // Login with username and password log.info("Try logging in as {}",LoginType.USERNAME_CODE); this.checkUsernameCode(loginData.getUsername(),loginData.getCommonLoginVerifyCode()); userDetails = myUserDetailService.loadUserByUsername(loginData.getUsername()); }else if (LoginType.PHONE_CODE.equals(loginType)) { // Login with verification code log.info("Try logging in as {}",LoginType.PHONE_CODE); this.checkPhoneCode(loginData.getPhone(),loginData.getPhoneVerifyCode()); userDetails = myUserDetailService.loadUserByPhone(loginData.getPhone()); }else if (LoginType.THIRD_PLATFORM.equals(loginType)) { // Platform login log.info("Try logging in as {}",LoginType.THIRD_PLATFORM); userDetails = myUserDetailService.loadByThirdPlatformId(loginData.getThirdPlatformType(),loginData.getThirdPlatformId()); }else { throw new AuthenticationServiceException("Unsupported login methods"); } // The authentication succeeded return this.createSuccessAuthentication(userDetails,myAuthenticationToken,userDetails); } @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { return null; } // What kind of token class does this provider support @Override public boolean supports(Class authentication) { return (MyAuthenticationToken.class.isAssignableFrom(authentication)); } public void checkPhoneCode(String phone,String code) { // Todo verifies the mobile phone verification code if ("111111".equals(code)) { }else { throw new AuthenticationServiceException("Mobile phone verification code error"); }}public void checkUsernameCode(String codeId,String code) { // Todo check user name and password login if ("222222".equals(code)) { }else { throw new AuthenticationServiceException("Verification code error"); }}}Copy the code
-
Modifying the Security Configuration
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired MyUserDetailServiceImpl userDetailService; @Autowired BCryptPasswordEncoder passwordEncoder; @Autowired MyAuthenticationTokenProvider myAuthenticationTokenProvider; // Create your own AuthenticationManager @Override @Bean protected AuthenticationManager authenticationManager(a) throws Exception { ProviderManager manager = new ProviderManager(Collections.singletonList(myAuthenticationTokenProvider)); return manager; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailService); } @Bean MyAuthenticationProcessingFilter myAuthenticationProcessingFilter(a) throws Exception { MyAuthenticationProcessingFilter filter = new MyAuthenticationProcessingFilter(); filter.setAuthenticationSuccessHandler((request, response, authentication) -> { response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal(); userDetails.setPassword(null); HashMap<String, Object> result = new HashMap<>(); result.put("code"."000000"); result.put("msg"."Successful landing."); result.put("data",userDetails); String s = new ObjectMapper().writeValueAsString(result); out.write(s); out.flush(); out.close(); }); filter.setAuthenticationFailureHandler((request, response, exception) -> { response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); HashMap<String, Object> result = new HashMap<>(); result.put("code"."111111"); if (exception instanceof LockedException) { result.put("msg"."Account locked!"); } else if (exception instanceof DisabledException) { result.put("msg"."Account disabled, please contact administrator!"); } else if (exception instanceof BadCredentialsException) { result.put("msg"."Wrong user name or password, please re-enter!"); } else if (exception instanceof AuthenticationServiceException) { result.put("msg",exception.getMessage()); } out.write(new ObjectMapper().writeValueAsString(result)); out.flush(); out.close(); }); // Set your authenticationManager into the environment filter.setAuthenticationManager(authenticationManager()); filter.setFilterProcessesUrl("/toLogin"); return filter; } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .authenticated() .and() .csrf() .disable(); // Replace the authentication interceptor (authentication entry)http.addFilterAt(myAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); }}Copy the code
landing
-
No login mode is configured
request
{ "phone":"987654321"."phoneVerifyCode":"111111" } Copy the code
resp
{ "msg": "Login mode cannot be empty."."code": "111111" } Copy the code
-
Mobile phone number verification code login
request
{ "loginType":"phoneCode"."phone":"987654321"."phoneVerifyCode":"111111" } Copy the code
resp
{ "msg": "Successful landing."."code": "000000"."data": { "id": 2."phone": null."username": "Bill"."password": null."accountStatus": 1."roleList": [{"id": 1."name": "admin"."desc": null }, { "id": 2."name": "user"."desc": null}]."enabled": true."authorities": [{"authority": "admin" }, { "authority": "user"}]."accountNonExpired": true."credentialsNonExpired": true."accountNonLocked": true}}Copy the code
-
User name and password login
request
{ "loginType":"usernameCode"."username":"Zhang"."password":"123456"."commonLoginVerifyCode":"222222"} Copy the code
resp
{ "msg": "Successful landing."."code": "000000"."data": { "id": 1."phone": null."username": "Zhang"."password": null."accountStatus": 1."roleList": [{"id": 1."name": "admin"."desc": null }, { "id": 2."name": "user"."desc": null}]."enabled": true."authorities": [{"authority": "admin" }, { "authority": "user"}]."accountNonExpired": true."credentialsNonExpired": true."accountNonLocked": true }} Copy the code
-
The third-party openId is logged in
request
{ "loginType":"thirdPlatform"."thirdPlatformType":"WeChat"."thirdPlatformId":"qwqwqqwqwqw"} Copy the code
resp
{ "msg": "Successful landing."."code": "000000"."data": { "id": 1."phone": null."username": "Zhang"."password": null."accountStatus": 1."roleList": [{"id": 1."name": "admin"."desc": null }, { "id": 2."name": "user"."desc": null}]."enabled": true."authorities": [{"authority": "admin" }, { "authority": "user"}]."accountNonExpired": true."credentialsNonExpired": true."accountNonLocked": true }} Copy the code
This is a relatively simple demo, the actual use of the process, but also need to optimize. For example, we can provide a variety of providers to handle various login methods.