前言

总所周知,SpringSecurity登录已经配置好了它的处理逻辑,并没有给我们开发者提供自定义验证码的处理逻辑。但我们可以通过以下方式来进行验证码的验证。

步骤

我们用的是captcha来生成我们的验证码。所以需要导入以下依赖。

<!--验证码-->
<dependency>
    <groupId>com.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
    <version>1.6.2</version>
</dependency>

然后通过一个Controller来生成一个返回验证码图片的方法。

@GetMapping(value = "/code",produces = MediaType.IMAGE_JPEG_VALUE)
public void getCode(HttpSession session)throws Exception{
    HttpServletResponse response = ServletUtil.getResponse();
    ServletOutputStream outputStream = response.getOutputStream();
    // 我们用的是两位数运算的验证码
    ArithmeticCaptcha captcha = new ArithmeticCaptcha(170, 60);
    // 设置两位数运算
    captcha.setLen(2);
    // 获取运算的结果
    String result = captcha.text();
    // 将运算结果存到session
    session.setAttribute("code",result);
    // 将生成的验证码输出
    captcha.out(outputStream);
}

然后增加一个验证码校验过滤器。Spring security的表单验证是通过过滤器链中的 UsernamePasswordAuthenticationFilter 来完成的,我们增加的验证码过滤器应该插在 UsernamePasswordAuthenticationFilter 之前,如果验证码校验不通过,直接返回,无需进行账户密码的校验。

首先定义一个自定义的验证码错误异常类。并且继承AuthenticationException,让SpringSecurity来帮我们捕获异常,并且处理。

代码如下:

public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String message) {
        super(message);
    }
}

然后我们自定义一个 ValidateCodeFilter 来做验证码的过滤器,该过滤器将继承 OncePerRequestFilter,这个可以保证每次请求只调用一次该过滤器,因为我们调用一次就够了。

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    @Resource
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Resource
    ObjectMapper objectMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws ServletException, IOException {
        if (StringUtils.equals("/login", req.getRequestURI()) && ServletUtil.getMethod().equals("POST")) {
            try {
                // 验证码验证逻辑
                validateCodeMethod();
            } catch (ValidateCodeException e) {
                // 将异常交给Spring Security来处理
                authenticationFailureHandler.onAuthenticationFailure(req, resp, e);
                return;
            }
        }
        // 放行
        chain.doFilter(req, resp);
    }

    public void validateCodeMethod(){
        HttpServletRequest req = ServletUtil.getRequest();
        // 获取前端请求的验证码
        String code = req.getParameter("code");
        // 获取Session中正确的code
        String successCode = (String) req.getSession().getAttribute("code");
        if(StringUtils.isEmpty(code)){
            // 抛出我们的自定义异常
            throw new ValidateCodeException("验证码不能为空");
        }
        if(!successCode.equals(code)){
            throw new ValidateCodeException("验证码错误");
        }
    }
}

然后我们只需要把我们的自定义验证码验证过滤器插入到UsernamePasswordAuthenticationFilter 就可以了。

@Resource
private ValidateCodeFilter validateCodeFilter;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
}

然后就完成了,可以去页面看看效果。

效果

这只是最简单的一个实现。当然还有更好的方法~

最后修改:2021 年 03 月 28 日 06 : 13 PM
要饭啦!!!赞赏一点吧!!!