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都是同理,如果需要都可以自行擴展。東西不多,希望對你有所幫助,歡迎留言交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM