SpringBoot 集成 Spring Security(8)——短信验证码登录

经过前面七章的学习,我们已经算入门 Spring Security 了,下面我们学习如何对 Spring Security 进行扩展,来实现短信验证码方式登录。

注意:

为了方便讲解,本篇文章代码直接在 《SpringBoot集成Spring Security(1)——入门程序》 上进行开发。

并且为了省去与本篇主题无关的代码,短信验证码只是一个模拟。如果你需要具体的实际例子,在下面的源码链接中除了包括每一章的代码外,还包括从头到尾的完整整合代码,方便大家参考学习。

源码地址:https://github.com/jitwxs/blog-sample/tree/master/springboot-security

一、理论说明

在开始编码前,先理解下短信验证码的实现流程。如果你能对《SpringBoot集成Spring Security(7)——认证流程》这篇文章有一定的了解的话,那么这篇文章的学习你会轻松许多。

1.1 用户名密码登录逻辑

废话不多说,在上一篇文章中,以标准的用户名密码登录为例,讲解了整个认证流程。大致流程如下:

  1. 先进入 UsernamePasswordAuthenticationFilter 中,根据输入的用户名和密码信息,构造出一个暂时没有鉴权的 UsernamePasswordAuthenticationToken,并将 UsernamePasswordAuthenticationToken 交给 AuthenticationManager 处理。
  2. AuthenticationManager 本身并不做验证处理,他通过 for-each 遍历找到符合当前登录方式的一个 AuthenticationProvider,并交给它进行验证处理,对于用户名密码登录方式,这个 Provider 就是 DaoAuthenticationProvider
  3. 在这个 Provider 中进行一系列的验证处理,如果验证通过,就会重新构造一个添加了鉴权的 UsernamePasswordAuthenticationToken,并将这个 token 传回到 UsernamePasswordAuthenticationFilter 中。
  4. 在该 Filter 的父类 AbstractAuthenticationProcessingFilter 中,会根据上一步验证的结果,跳转到 successHandler 或者是 failureHandler。
Spring Security 认证流程(部分)

1.2 短信验证码登录逻辑

我们可以仿照用户名密码登录的逻辑,来实现短信验证码的登陆逻辑。

  1. 用户名密码登录有个 UsernamePasswordAuthenticationFilter ,我们搞一个 SmsAuthenticationFilter,代码粘过来改一改。
  2. 用户名密码登录需要 UsernamePasswordAuthenticationToken,我们搞一个 SmsAuthenticationToken,代码粘过来改一改。
  3. 用户名密码登录需要 DaoAuthenticationProvider,我们模仿它也 implenments AuthenticationProvider,叫做 SmsAuthenticationProvider

我们自己搞了上面三个类以后,想要实现的效果如上图所示。当我们使用短信验证码登录的时候:

  1. 先经过 SmsAuthenticationFilter,构造一个没有鉴权的 SmsAuthenticationToken,然后交给 AuthenticationManager 处理。
  2. AuthenticationManager 通过 for-each 挑选出一个合适的 provider 进行处理,当然我们希望这个 provider 要是 SmsAuthenticationProvider
  3. 验证通过后,重新构造一个有鉴权的 SmsAuthenticationToken,并返回给 SmsAuthenticationFilter
  4. filter 根据上一步的验证结果,跳转到成功或者失败的处理逻辑。

二、代码实战

请通过文章开头 github 链接下载第一章代码,或者参看《SpringBoot集成Spring Security(1)——入门程序》初始化项目,这里就不再赘述了。

2.1 SmsAuthenticationToken

首先我们编写 SmsAuthenticationToken,这里直接参考 UsernamePasswordAuthenticationToken 源码,直接粘过来,改一改。

步骤:

  1. principal 原本代表用户名,这里保留,只是代表了手机号码。
  2. credentials 原本代码密码,短信登录用不到,直接删掉。
  3. SmsCodeAuthenticationToken() 两个构造方法一个是构造没有鉴权的,一个是构造有鉴权的。
  4. 剩下的几个方法去除无用属性即可。
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;

import java.util.Collection;

/**
 * 短信登录 AuthenticationToken,模仿 UsernamePasswordAuthenticationToken 实现
 * @author jitwxs
 * @since 2019/1/9 13:47
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
   

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    /**
     * 在 UsernamePasswordAuthenticationToken 中该字段代表登录的用户名,
     * 在这里就代表登录的手机号码
     */
    private final Object principal;

    /**
     * 构建一个没有鉴权的 SmsCodeAuthenticationToken
     */
    public SmsCodeAuthenticationToken(Object principal) {
   
        super(null);
        this.principal = principal;
        setAuthenticated(false);
    }

    /**
     * 构建拥有鉴权的 SmsCodeAuthenticationToken
     */
    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
   
        super(authorities);
        this.principal = principal;
        // must use super, as we override
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
   
        return null;
    }

    @Override
    public Object getPrincipal() {
   
        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() {
   
        super.eraseCredentials();
    }
}

2.2 SmsAuthenticationFilter

然后编写 SmsAuthenticationFilter,参考 UsernamePasswordAuthenticationFilter 的源码,直接粘过来,改一改。

步骤:

  1. 原本的静态字段有 username 和 password,都干掉,换成我们的手机号字段。
  2. SmsCodeAuthenticationFilter() 中指定了这个 filter 的拦截 Url,我指定为 post 方式的 /sms/login
  3. 剩下来的方法把无效的删删改改就好了。
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 短信登录的鉴权过滤器,模仿 UsernamePasswordAuthenticationFilter 实现
 * @author jitwxs
 * @since 2019/1/9 13:52
 */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
   
    /**
     * form表单中手机号码的字段name
     */
    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";

    private<
评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值