服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|

服务器之家 - 编程语言 - Java教程 - SpringBoot + SpringSecurity 短信验证码登录功能实现

SpringBoot + SpringSecurity 短信验证码登录功能实现

2021-05-09 12:27whyalwaysmea Java教程

这篇文章主要介绍了SpringBoot + SpringSecurity 短信验证码登录功能实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

实现原理

在之前的文章中,我们介绍了普通的帐号密码登录的方式: SpringBoot + Spring Security 基本使用及个性化登录配置。 但是现在还有一种常见的方式,就是直接通过手机短信验证码登录,这里就需要自己来做一些额外的工作了。

SpringSecurity认证流程详解有一定了解的都知道,在帐号密码认证的过程中,涉及到了以下几个类:UsernamePasswordAuthenticationFilter(用于请求参数获取),UsernamePasswordAuthenticationToken(表示用户登录信息),ProviderManager(进行认证校验),

因为是通过的短信验证码登录,所以我们需要对请求的参数,认证过程,用户登录Token信息进行一定的重写。
当然验证码的过程我们应该放在最前面,如果图形验证码的实现一样。这样的做法的好处是:将验证码认证该过程解耦出来,让其他接口也可以使用到。

基本实现

验证码校验

短信验证码的功能实现,其实和图形验证码的原理是一样的。只不过一个是返回给前端一个图片,一个是给用户发送短消息,这里只需要去调用一下短信服务商的接口就好了。更多的原理可以参考 SpringBoot + SpringSecurity 实现图形验证码功能

AuthenticationToken

在使用帐号密码登录的时候,UsernamePasswordAuthenticationToken里面包含了用户的帐号,密码,以及其他的是否可用等状态信息。我们是通过手机短信来做登录,所以就没有密码了,这里我们就直接将UsernamePasswordAuthenticationToken的代码copy过来,把密码相关的信息去掉就可以了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
 
  private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
 
  private final Object principal;
 
  public SmsCodeAuthenticationToken(String mobile) {
    super(null);
    this.principal = mobile;
    setAuthenticated(false);
  }
 
  public SmsCodeAuthenticationToken(Object principal,
                   Collection<? extends GrantedAuthority> authorities) {
    super(authorities);
    this.principal = principal;
    super.setAuthenticated(true); // must use super, as we override
  }
 
  public Object getCredentials() {
    return null;
  }
 
  public Object getPrincipal() {
    return this.principal;
  }
 
  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() {
    super.eraseCredentials();
  }
}

AuthenticationFilter

在帐户密码登录的流程中,默认使用的是UsernamePasswordAuthenticationFilter,它的作用是从请求中获取帐户、密码,请求方式校验,生成AuthenticationToken。这里我们的参数是有一定改变的,所以还是老方法,copy过来进行简单的修改

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  // 请求参数key
  private String mobileParameter = SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE;
  // 是否只支持POST
  private boolean postOnly = true;
 
  public SmsCodeAuthenticationFilter() {
    // 请求接口的url
    super(new AntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, "POST"));
  }
 
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException {
    if (postOnly && !request.getMethod().equals("POST")) {
      throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    }
    // 根据请求参数名,获取请求value
    String mobile = obtainMobile(request);
    if (mobile == null) {
      mobile = "";
    }
    mobile = mobile.trim();
 
    // 生成对应的AuthenticationToken
    SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
 
    setDetails(request, authRequest);
 
    return this.getAuthenticationManager().authenticate(authRequest);
  }
 
  /**
   * 获取手机号
   */
  protected String obtainMobile(HttpServletRequest request) {
    return request.getParameter(mobileParameter);
  }
  // 省略不相关代码
}

Provider

在帐号密码登录的过程中,密码的正确性以及帐号是否可用是通过DaoAuthenticationProvider来校验的。我们也应该自己实现一个Provier

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
 
  private UserDetailsService userDetailsService;
 
  /**
   * 身份逻辑验证
   * @param authentication
   * @return
   * @throws AuthenticationException
   */
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 
    SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
 
    UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
 
    if (user == null) {
      throw new InternalAuthenticationServiceException("无法获取用户信息");
    }
 
    SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
 
    authenticationResult.setDetails(authenticationToken.getDetails());
 
    return authenticationResult;
  }
 
  @Override
  public boolean supports(Class<?> authentication) {
    return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
  }
 
  public UserDetailsService getUserDetailsService() {
    return userDetailsService;
  }
 
  public void setUserDetailsService(UserDetailsService userDetailsService) {
    this.userDetailsService = userDetailsService;
  }
}

配置

主要的认证流程就是通过以上四个过程实现的, 这里我们再降它们配置一下就可以了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
 
  @Autowired
  private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
 
  @Autowired
  private AuthenticationFailureHandler myAuthenticationFailureHandler;
 
  @Autowired
  private UserDetailsService userDetailsService;
 
  @Override
  public void configure(HttpSecurity http) throws Exception {
 
    SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
    smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
    smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
 
    SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
    smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
 
    http.authenticationProvider(smsCodeAuthenticationProvider)
        .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 
  }
}
 
// BrowerSecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
  http.apply(smsCodeAuthenticationSecurityConfig);
}

代码下载

Spring-Security

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/u013435893/article/details/79684027

延伸 · 阅读

精彩推荐