SpringBoot + SpringSecurity 短信验证码登录功能实现
实现原理
在之前的文章中,我们介绍了普通的帐号密码登录的方式:SpringBoot+SpringSecurity基本使用及个性化登录配置。但是现在还有一种常见的方式,就是直接通过手机短信验证码登录,这里就需要自己来做一些额外的工作了。
对SpringSecurity认证流程详解有一定了解的都知道,在帐号密码认证的过程中,涉及到了以下几个类:UsernamePasswordAuthenticationFilter(用于请求参数获取),UsernamePasswordAuthenticationToken(表示用户登录信息),ProviderManager(进行认证校验),
因为是通过的短信验证码登录,所以我们需要对请求的参数,认证过程,用户登录Token信息进行一定的重写。
当然验证码的过程我们应该放在最前面,如果图形验证码的实现一样。这样的做法的好处是:将验证码认证该过程解耦出来,让其他接口也可以使用到。
基本实现
验证码校验
短信验证码的功能实现,其实和图形验证码的原理是一样的。只不过一个是返回给前端一个图片,一个是给用户发送短消息,这里只需要去调用一下短信服务商的接口就好了。更多的原理可以参考SpringBoot+SpringSecurity实现图形验证码功能
AuthenticationToken
在使用帐号密码登录的时候,UsernamePasswordAuthenticationToken里面包含了用户的帐号,密码,以及其他的是否可用等状态信息。我们是通过手机短信来做登录,所以就没有密码了,这里我们就直接将UsernamePasswordAuthenticationToken的代码copy过来,把密码相关的信息去掉就可以了
publicclassSmsCodeAuthenticationTokenextendsAbstractAuthenticationToken{ privatestaticfinallongserialVersionUID=SpringSecurityCoreVersion.SERIAL_VERSION_UID; privatefinalObjectprincipal; publicSmsCodeAuthenticationToken(Stringmobile){ super(null); this.principal=mobile; setAuthenticated(false); } publicSmsCodeAuthenticationToken(Objectprincipal, Collectionauthorities){ super(authorities); this.principal=principal; super.setAuthenticated(true);//mustusesuper,asweoverride } publicObjectgetCredentials(){ returnnull; } publicObjectgetPrincipal(){ returnthis.principal; } publicvoidsetAuthenticated(booleanisAuthenticated)throwsIllegalArgumentException{ if(isAuthenticated){ thrownewIllegalArgumentException( "Cannotsetthistokentotrusted-useconstructorwhichtakesaGrantedAuthoritylistinstead"); } super.setAuthenticated(false); } @Override publicvoideraseCredentials(){ super.eraseCredentials(); } }
AuthenticationFilter
在帐户密码登录的流程中,默认使用的是UsernamePasswordAuthenticationFilter,它的作用是从请求中获取帐户、密码,请求方式校验,生成AuthenticationToken。这里我们的参数是有一定改变的,所以还是老方法,copy过来进行简单的修改
publicclassSmsCodeAuthenticationFilterextendsAbstractAuthenticationProcessingFilter{ //请求参数key privateStringmobileParameter=SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE; //是否只支持POST privatebooleanpostOnly=true; publicSmsCodeAuthenticationFilter(){ //请求接口的url super(newAntPathRequestMatcher(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,"POST")); } publicAuthenticationattemptAuthentication(HttpServletRequestrequest,HttpServletResponseresponse) throwsAuthenticationException{ if(postOnly&&!request.getMethod().equals("POST")){ thrownewAuthenticationServiceException("Authenticationmethodnotsupported:"+request.getMethod()); } //根据请求参数名,获取请求value Stringmobile=obtainMobile(request); if(mobile==null){ mobile=""; } mobile=mobile.trim(); //生成对应的AuthenticationToken SmsCodeAuthenticationTokenauthRequest=newSmsCodeAuthenticationToken(mobile); setDetails(request,authRequest); returnthis.getAuthenticationManager().authenticate(authRequest); } /** *获取手机号 */ protectedStringobtainMobile(HttpServletRequestrequest){ returnrequest.getParameter(mobileParameter); } //省略不相关代码 }
Provider
在帐号密码登录的过程中,密码的正确性以及帐号是否可用是通过DaoAuthenticationProvider来校验的。我们也应该自己实现一个Provier
publicclassSmsCodeAuthenticationProviderimplementsAuthenticationProvider{ privateUserDetailsServiceuserDetailsService; /** *身份逻辑验证 *@paramauthentication *@return *@throwsAuthenticationException */ @Override publicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{ SmsCodeAuthenticationTokenauthenticationToken=(SmsCodeAuthenticationToken)authentication; UserDetailsuser=userDetailsService.loadUserByUsername((String)authenticationToken.getPrincipal()); if(user==null){ thrownewInternalAuthenticationServiceException("无法获取用户信息"); } SmsCodeAuthenticationTokenauthenticationResult=newSmsCodeAuthenticationToken(user,user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); returnauthenticationResult; } @Override publicbooleansupports(Class>authentication){ returnSmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } publicUserDetailsServicegetUserDetailsService(){ returnuserDetailsService; } publicvoidsetUserDetailsService(UserDetailsServiceuserDetailsService){ this.userDetailsService=userDetailsService; } }
配置
主要的认证流程就是通过以上四个过程实现的,这里我们再降它们配置一下就可以了
@Component publicclassSmsCodeAuthenticationSecurityConfigextendsSecurityConfigurerAdapter{ @Autowired privateAuthenticationSuccessHandlermyAuthenticationSuccessHandler; @Autowired privateAuthenticationFailureHandlermyAuthenticationFailureHandler; @Autowired privateUserDetailsServiceuserDetailsService; @Override publicvoidconfigure(HttpSecurityhttp)throwsException{ SmsCodeAuthenticationFiltersmsCodeAuthenticationFilter=newSmsCodeAuthenticationFilter(); smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler); SmsCodeAuthenticationProvidersmsCodeAuthenticationProvider=newSmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(smsCodeAuthenticationProvider) .addFilterAfter(smsCodeAuthenticationFilter,UsernamePasswordAuthenticationFilter.class); } } //BrowerSecurityConfig.java @Override protectedvoidconfigure(HttpSecurityhttp)throwsException{ http.apply(smsCodeAuthenticationSecurityConfig); }
代码下载
Spring-Security
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。