spring security 自定义短信验证登录


  在之前的博客中我们实现了基于验证码的登陆方式。但是我们仅仅实现了通过图片验证码的登录,现在我们基于security 实现一下基于 短信验证码的登录。

  基于之前对于 security 的了解,我们知道了要实现一个验证的基本流程,其中最重要的是  AbstractAuthenticationToken(令牌类)、AuthenticationProvider(认证类)、AbstractAuthenticationProcessingFilter (过滤器类)这三个类。

  我们可以基于用户密码的登陆方式来实现,对于用户名密码的登录涉及的三个类为:UsernamePasswordAuthenticationToken、UsernamePasswordAuthenticationFilter、而 AuthenticationProvider 在之前我们也是自定义的 ,修改一下就可以用。

1. 编写 SmsCodeAuthenticationToken

/** * Create with IntelliJ IDEA * User: Wuzhenzhao * Date: 2019/3/13 * Time: 18:13 * Description: 参考 UsernamePasswordAuthenticationToken 写 * 封装登录信息 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; // ~ Instance fields // ================================================================================================

    private final Object principal; //这个在 UsernamePasswordAuthenticationToken 是登录密码,不需要 干掉。
    private Object credentials; // ~ Constructors // ===================================================================================================

    /** * This constructor can be safely used by any code that wishes to create a * <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()} * will return <code>false</code>. * */
    public SmsCodeAuthenticationToken(String mobile) { super(null); this.principal = mobile; setAuthenticated(false); } /** * This constructor should only be used by <code>AuthenticationManager</code> or * <code>AuthenticationProvider</code> implementations that are satisfied with * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>) * authentication token. * * @param principal * @param authorities */
    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; super.setAuthenticated(true); // must use super, as we override
 } // ~ Methods // ========================================================================================================

    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(); } }

 2. SmsCodeAuthenticationProvider ,用于短信登陆的验证

public class SmsCodeAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; /* * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * authenticate(org.springframework.security.core.Authentication) */ @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; } /* * (non-Javadoc) * * @see org.springframework.security.authentication.AuthenticationProvider# * supports(java.lang.Class) */ @Override public boolean supports(Class<?> authentication) { return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication); } public UserDetailsService getUserDetailsService() { return userDetailsService; } public void setUserDetailsService(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } }

3.  SmsCodeAuthenticationFilter  拦截短信登陆请求

/** * Create with IntelliJ IDEA * User: Wuzhenzhao * Date: 2019/3/13 * Time: 18:13 * Description: 参考 UsernamePasswordAuthenticationFilter */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // ~ Static fields/initializers // =====================================================================================
    /** * 发送短信验证码 或 验证短信验证码时,传递手机号的参数的名称 */
    private String mobileParameter = "mobile"; private boolean postOnly = true; // ~ Constructors // ===================================================================================================

    public SmsCodeAuthenticationFilter() { /** * 默认的手机验证码登录请求处理url * http://localhost:8889/code/sms?mobile=13888888888 */ super(new AntPathRequestMatcher("/authentication/mobile", "POST")); } // ~ Methods // ========================================================================================================

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String mobile = obtainMobile(request); if (mobile == null) { mobile = ""; } mobile = mobile.trim(); SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile); // Allow subclasses to set the "details" property
 setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * 获取手机号 */
    protected String obtainMobile(HttpServletRequest request) { return request.getParameter(mobileParameter); } /** * Provided so that subclasses may configure what is put into the * authentication request's details property. * * @param request * that an authentication request is being created for * @param authRequest * the authentication request object that should have its details * set */
    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) { authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); } /** * Sets the parameter name which will be used to obtain the username from * the login request. * * @param usernameParameter * the parameter name. Defaults to "username". */
    public void setMobileParameter(String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); this.mobileParameter = usernameParameter; } /** * Defines whether only HTTP POST requests will be allowed by this filter. * If set to true, and an authentication request is received which is not a * POST request, an exception will be raised immediately and authentication * will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method * will be called as if handling a failed authentication. * <p> * Defaults to <tt>true</tt> but may be overridden by subclasses. */
    public void setPostOnly(boolean postOnly) { this.postOnly = postOnly; } public final String getMobileParameter() { return mobileParameter; } }

4.在 security 配置类中注入:

@Configuration @EnableWebSecurity// 开启Security
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启Spring方法级安全
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationProvider myAuthenticationProvider; @Autowired private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler; @Autowired private MyUserDetailService myUserDetailService; // 自定义认证配置
 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(myAuthenticationProvider); SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider(); smsCodeAuthenticationProvider.setUserDetailsService(myUserDetailService); auth.authenticationProvider(smsCodeAuthenticationProvider); } @Override protected void configure(HttpSecurity http) throws Exception {      // .......省略部分代码
        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter(); smsCodeAuthenticationFilter.setAuthenticationManager(this.authenticationManager()); smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler); smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenctiationFailureHandler); http.addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }

  对于提交登录得时候验证码的校验逻辑在 博客 :https://www.cnblogs.com/wuzhenzhao/p/13169023.html 中提到的 AbstractValidateCodeProcessor 进行验证。这样子先进行一个短信的发送,然后模拟一下登录就可用实现了.


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM