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

网友投稿 290 2023-07-30

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

实现原理

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

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

因为是通过的短信验证码登录,所以我们需要对请求的参数,认证过程,用户登录Token信息进行一定的重写。

当然验证码的过程我们应该放在最前面,如果图形验证码的实现一样。这样的做法的好处是:将验证码认证该过程解耦出来,让其他接口也可以使用到。

基本实现

验证码校验

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

AuthenticationToken

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

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {

private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

private final Object principal;

public SmsCodeAuthenticationToken(String mobile) {

super(nuhttp://ll);

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过来进行简单的修改

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 supCXqdWported: " + request.getMethod());

}

// 根据请求参数名,获取请求value

String mobile = obtainMobile(request);

if (mobile == null) {

mobile = "";

}

mohttp://bile = 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

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;

}

}

配置

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

@Component

public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter {

@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

http://

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java实现上传文件图片到指定服务器目录
下一篇:浅谈Webpack打包优化技巧
相关文章

 发表评论

暂时没有评论,来抢沙发吧~