Spring-Security 自定义Filter完成验证码校验

 Spring-Security的功能主要是由一堆Filter构成过滤器链来实现,每个Filter都会完成自己的一部分工作。我今天要做的是对UsernamePasswordAuthenticationFilter进行扩展,新增一个Filter,完成对登录页面的校验码的验证。下面先给一张过滤器的说明,接下来讲自定义的登录验证Filter。

 https://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/reference/htmlsingle/#ns-web-advanced

   一、扩展AbstractAuthenticationProcessingFilter,实现MyUsernamePasswordAuthenticationFilter。

package simm.spring.web.config;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

public class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter  {
     // 是否开启验证码功能
    private boolean isOpenValidateCode = true;

    public static final String VALIDATE_CODE = "validateCode";

    public MyUsernamePasswordAuthenticationFilter() {
        super(new AntPathRequestMatcher("/user/login.do", "POST"));
        SimpleUrlAuthenticationFailureHandler failedHandler = (SimpleUrlAuthenticationFailureHandler)getFailureHandler();
        failedHandler.setDefaultFailureUrl("/user/login.do?validerror");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res=(HttpServletResponse)response;

        if (!requiresAuthentication(req, res)) {
            chain.doFilter(request, response);
            return;
        }
        if (isOpenValidateCode) {
            if(!checkValidateCode(req, res))return;
        }
        //保存一些session信息
        HttpSession session = req.getSession();
        session.setAttribute(VALIDATE_CODE, "mytest");
        chain.doFilter(request,response);
    }  

    /**
     * 覆盖授权验证方法,这里可以做一些自己需要的session设置操作
     */
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        return null;
    }

    protected boolean checkValidateCode(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
        HttpSession session = request.getSession();

        String sessionValidateCode = obtainSessionValidateCode(session);
        sessionValidateCode = "1234";// 做个假的验证码;
        // 让上一次的验证码失效
        session.setAttribute(VALIDATE_CODE, null);
        String validateCodeParameter = obtainValidateCodeParameter(request);
        if (StringUtils.isEmpty(validateCodeParameter) || !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {
            unsuccessfulAuthentication(request, response, new InsufficientAuthenticationException("输入的验证码不正确"));
            return false;
        }
        return true;
    }

    private String obtainValidateCodeParameter(HttpServletRequest request) {
        Object obj = request.getParameter(VALIDATE_CODE);
        return null == obj ? "" : obj.toString();
    }

    protected String obtainSessionValidateCode(HttpSession session) {
        Object obj = session.getAttribute(VALIDATE_CODE);
        return null == obj ? "" : obj.toString();
    }
}

  代码解读

  1、为Filter指定请求地址过滤器,用于拦截登录请求。调用AbstractAuthenticationProcessingFilter.requiresAuthentication方法。

  2、指定验证失败的跳转页面

  

  3、验证码的测试代码。假的验证码1234,与页面参数比对后,如果不相等则抛出"输入的验证码不正确"的异常。

  

  4、验证通过,继续执行后续的Filter链。否则退出请求处理逻辑。这个Filter只处理验证码的校验逻辑,用户名密码的验证交给后面的UsernamePasswordAuthenticationFilter来处理。

  

   二、向HttpSecurity的Filter链上插入自定义的Filter,插入到UsernamePasswordAuthenticationFilter的位置上。插入方法有addFilterBefore,addFilterAt,addFilterAfter。这个地方需要注意使用addFilterAt并不是说能替换掉原有的Filter,事实上框架原有的Filter在启动HttpSecurity配置的过程中,都由框架完成了其一定程度上固定的配置,是不允许更改替换的。根据测试结果来看,调用addFilterAt方法插入的Filter,会在这个位置上的原有Filter之前执行。

@Override
    protected void configure(HttpSecurity http) throws Exception {
        String doUrl = "/**/*.do";
        http
         .addFilterAt(new MyUsernamePasswordAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)

  三、登录方法添加对验证码错误回调的拦截

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(@RequestParam(value = "error", required = false) String error,
            @RequestParam(value = "validerror", required = false) String validerror,
            @RequestParam(value = "logout", required = false) String logout,Model model) {
        if (error != null) {
            model.addAttribute("msg", "用户名或密码错误!");
        }
        if(validerror!=null){
            model.addAttribute("msg", "验证码错误!");
        }
        if (logout != null) {
            model.addAttribute("msg", "成功退出!");
        }
        return "user/login";
    }

     四、测试结果展示

  

  以上就是我对登录功能的自定义Filter实现。其他Filter都是同理,如果需要都可以自行扩展。东西不多,希望对你有所帮助,欢迎留言交流。

时间: 2024-08-28 18:36:20

Spring-Security 自定义Filter完成验证码校验的相关文章

单点登录CAS与Spring Security集成(数据库验证,向客户端发送更多信息)

准备工作 CAS server从网上直接下载下来,里面有一个cas-server-webapp的工程,使用Maven命令构建,导入到Eclipse中,便可以直接使用,cas server我使用的是3.5.2版本.客户端,我是使用以前的工程,只要是Web工程就行,cas-client使用的3.2.1,Spring Security使用的是3.1.4,记得Spring Security的3.1.2版本和CAS集成时,当需要CAS Server传比较多的信息给客户端时,客户端的Spring Secur

spring security自定义指南

序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义AuthenticationProvider 自定义AccessDecisionManager 自定义securityMetadataSource 自定义access访问控制 自定义authenticationEntryPoint 自定义多个WebSecurityConfigurerAdapter 自定义User

Spring Security 自定义登录认证(二)

一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Security中有默认的密码加密方式以及登录用户认证校验,但小编这里选择自定义是为了方便以后业务扩展,比如系统默认带一个超级管理员,当认证时识别到是超级管理员账号登录访问时给它赋予最高权限,可以访问系统所有api接口,或在登录认证成功后存入token以便用户访问系统其它接口时通过token认证用户权限

redis jwt spring boot spring security 实现api token 验证

文章地址:http://www.haha174.top/article/details/258083 项目源码:https://github.com/haha174/jwt-token.git 具体的实际效果可以看考这里 目前已经部署一个 个人测试机器上面: http://cloud.codeguoj.cn/api-cloud-server/swagger-ui.html#!/token45controller/loginUsingPOST 相信很多人都调用过api, 一般的大致基本步骤都是先用

Spring Security简单的登陆验证授权

Spring Security的介绍就省略了,直接记录一下登陆验证授权的过程. Spring Security的几个重要词 1.SecurityContextHolder:是安全上下文容器,可以在此得知操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限-这些都被保存在SecurityContextHolder中. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

关于spring security自定义sessionRegistry不工作的原因简析

最近调了一下spring security的集群session共享,用到了自定义的SessionRegistry,却发现怎么也不工作,翻了翻stackoverfllow,也没找到靠谱的办法,最后自己debug,找到了问题所在 本文基于 Spring3.1.5,Spring security 2.0.4 最开始配置如下: <beans:bean id="sessionRegistry"      class="com.shop.core.security.support.

关于spring security自定义sessionRegistry

最近调了一下spring security的集群session共享,用到了自定义的SessionRegistry,却发现怎么也不工作,翻了翻stackoverfllow,也没找到靠谱的办法,最后自己debug,找到了问题所在 本文基于 Spring3.1.5,Spring security 2.0.4 最开始配置如下: 1 2 3 4 5 6 7 8 9 10 11 <beans:bean id="sessionRegistry"      class="com.sho

02 spring security 自定义用户认证流程

1. 自定义登录页面 (1)首先在static目录下面创建login.html       注意: springboot项目默认可以访问resources/resources, resources/staic, resources/public目录下面的静态文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页

Spring Security 自定义登录验证与自定义回调地址

1 配置文件 security-ns.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www