Spring Security -- 短信验证码登录(转载) Spring Security -- 添加图形验证码 Spring Security -- 添加图形验证码

Spring Security -- 添加图形验证码一节中,我们已经实现了基于Spring Boot + Spring Security的账号密码登录,并集成了图形验证码功能。当前另一种非常常见的网站登录方式为手机短信验证码登录,但Spring Security默认只提供了账号密码的登录认证逻辑,所以要实现手机短信验证码登录认证功能,我们需要模仿Spring Security账号密码登录逻辑代码来实现一套自己的认证逻辑。

一、短信验证码生成

我们在Spring Security -- 添加图形验证码的基础上来集成短信验证码登录的功能。

1、SmsCode实体类

和图形验证码类似,我们先定义一个短信验证码对象SmsCode:

package com.goldwind.entity;

import lombok.Data;

import java.time.LocalDateTime;

/**
 * @Author: zy
 * @Description: 手机验证码实体类
 * @Date: 2020-2-9
 */
@Data
public class SmsCode {
    /**
     * code验证码
     */
    private String code;

    /**
     * 过期时间 单位秒
     */
    private LocalDateTime expireTime;

    /**
     * 判断验证码是否过期
     * @return
     */
    public boolean isExpire() {
        return LocalDateTime.now().isAfter(expireTime);
    }

    /**
     * 构造函数
     * @param code
     * @param expireIn
     */
    public SmsCode(String code, int expireIn) {
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
    }

    /**
     * 构造函数
     * @param code
     * @param expireTime
     */
    public SmsCode(String code, LocalDateTime expireTime) {
        this.code = code;
        this.expireTime = expireTime;
    }

}

SmsCode对象包含了两个属性:code验证码和expireTime过期时间。isExpire方法用于判断短信验证码是否已过期。

2、ValidateCodeController

接着在ValidateCodeController中加入生成短信验证码相关请求对应的方法:

/**
     * 手机验证码
     */
    public final static String SESSION_KEY_SMS_CODE = "SESSION_KEY_SMS_CODE";


    /**
     * 用于生成手机验证码
     * @param request:请求
     * @param response:响应
     * @param mobile:手机号码
     * @throws IOException:异常
     */
    @RequestMapping("/sms")
    public void createSmsCode(HttpServletRequest request, HttpServletResponse response,@RequestParam String mobile) throws IOException {
        //生成手机验证码对象
        SmsCode smsCode = createSMSCode();
        //生成的验证码对象存储到Session中
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_SMS_CODE + mobile, smsCode);
        // 输出验证码到控制台代替短信发送服务
        System.out.println("您的登录验证码为:" + smsCode.getCode() + ",有效时间为60秒");
    }


    /**
     * 用于生成手机验证码对象
     * @return
     */
    private SmsCode createSMSCode() {
        String code = RandomStringUtils.randomNumeric(6);
        return new SmsCode(code, 60);
    }

这里我们使用createSMSCode方法生成了一个6位的纯数字随机数,有效时间为60秒。然后通过SessionStrategy对象的setAttribute方法将短信验证码保存到了Session中,对应的key为SESSION_KEY_SMS_CODE。

至此,短信验证码生成模块编写完毕,下面开始改造登录页面。

二、登录页

我们在登录页面中加入一个与手机短信验证码认证相关的Form表单:

<div id="content">
        <div id="box">
            <div class="title">短信验证码登录</div>
            <div class="input">
                <form name="f" action="/login" method="post">
                    <input type="text" placeholder="手机号" name="mobile" value="17777777777" required="required"/>
                    <br>
                    <input type="text" name="smsCode" placeholder="短信验证码" style="width: 50%;"/>
                    <a href="/code/sms?mobile=17777777777">发送验证码</a>
                    <br>
                    <input type="submit" value="登录" />
                </form>
            </div>
        </div>
    </div>

其中a标签的href属性值对应我们的短信验证码生成方法的请求URL。同时,我们需要在Spring Security中配置/code/sms路径免验证:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
                .authorizeRequests()    // 授权配置
                .antMatchers("/code/image","/code/sms")
                .permitAll()       // 无需认证的请求路径
                .anyRequest()       // 任何请求
                .authenticated()    //都需要身份认证
                .and()
                .formLogin()         // 或者httpBasic()
                .loginPage("/login")  // 指定登录页的路径
                .loginProcessingUrl("/login")  // 指定自定义form表单请求的路径
                .successHandler(authenticationSucessHandler)    // 处理登录成功
                .failureHandler(authenticationFailureHandler) // 处理登录失败
                // 必须允许所有用户访问我们的登录页(例如未验证的用户,否则验证流程就会进入死循环)
                // 这个formLogin().permitAll()方法允许所有用户基于表单登录访问/login这个page。
                .permitAll()
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository)  // 配置 token 持久化仓库
                .tokenValiditySeconds(3600)      // remember 过期时间,单为秒
                .userDetailsService(userDetailService)   // 处理自动登录逻辑
                .and()
                .logout()
                .permitAll()
                .and()
                //默认都会产生一个hiden标签 里面有安全相关的验证 防止请求伪造 这边我们暂时不需要 可禁用掉
                .csrf().disable();
    }

重启项目,访问http://localhost:8080/login

点击发送验证码,控制台输出如下:

您的登录验证码为:788974,有效时间为60秒

接下来开始实现使用短信验证码登录认证逻辑。

三、添加短信验证码认证

1、实现原理

在Spring Security中,使用用户名密码认证的过程大致如下图所示:

Spring Security使用UsernamePasswordAuthenticationFilter过滤器来拦截用户名密码认证请求,将用户名和密码封装成一个UsernamePasswordToken对象交给AuthenticationManager处理。AuthenticationManager将挑出一个支持处理该类型Token的AuthenticationProvider(这里为DaoAuthenticationProvider,AuthenticationProvider的其中一个实现类)来进行认证,认证过程中DaoAuthenticationProvider将调用UserDetailService的loadUserByUsername方法来处理认证,如果认证通过(即UsernamePasswordToken中的用户名和密码相符)则返回一个UserDetails类型对象,并将认证信息保存到Session中,认证后我们便可以通过Authentication对象获取到认证的信息了。

由于Spring Security并没用提供短信验证码认证的流程,所以我们需要仿照上面这个流程来实现:

在这个流程中,我们自定义了一个名为SmsAuthenticationFitler的过滤器来拦截短信验证码登录请求,并将手机号码封装到一个叫SmsAuthenticationToken的对象中。在Spring Security中,认证处理都需要通过AuthenticationManager来代理,所以这里我们依旧将SmsAuthenticationToken交由AuthenticationManager处理。接着我们需要定义一个支持处理SmsAuthenticationToken对象的SmsAuthenticationProvider,SmsAuthenticationProvider调用UserDetailService的loadUserByUsername方法来处理认证。与用户名密码认证不一样的是,这里是通过SmsAuthenticationToken中的手机号去数据库中查询是否有与之对应的用户,如果有,则将该用户信息封装到UserDetails对象中返回并将认证后的信息保存到Authentication对象中。

为了实现这个流程,我们需要定义SmsAuthenticationFitler、SmsAuthenticationToken和SmsAuthenticationProvider,并将这些组建组合起来添加到Spring Security中。下面我们来逐步实现这个过程。

2、定义SmsAuthenticationToken

猜你喜欢

转载自www.cnblogs.com/zyly/p/12287813.html