Spring Security 實戰乾貨:圖解用戶是如何登陸的

1. 前言

歡迎閱讀Spring Security 實戰乾貨系列文章,在集成Spring Security安全框架的時候咱們最早處理的可能就是根據咱們項目的實際須要來定製註冊登陸了,尤爲是Http登陸認證。根據之前的相關文章介紹,Http登陸認證由過濾器UsernamePasswordAuthenticationFilter 進行處理。咱們只有把這個過濾器搞清楚才能作一些定製化。今天咱們就簡單分析它的源碼和工做流程。java

2. UsernamePasswordAuthenticationFilter 源碼分析

UsernamePasswordAuthenticationFilter 繼承於AbstractAuthenticationProcessingFilter(另文分析)。它的做用是攔截登陸請求並獲取帳號和密碼,而後把帳號密碼封裝到認證憑據UsernamePasswordAuthenticationToken中,而後把憑據交給特定配置的AuthenticationManager去做認證。源碼分析以下:安全

public class UsernamePasswordAuthenticationFilter extends
      AbstractAuthenticationProcessingFilter {
    // 默認取帳戶名、密碼的key
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    // 能夠經過對應的set方法修改
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    // 默認只支持 POST 請求
    private boolean postOnly = true;
    
   //  初始化一個用戶密碼 認證過濾器  默認的登陸uri 是 /login 請求方式是POST
   public UsernamePasswordAuthenticationFilter() {
      super(new AntPathRequestMatcher("/login", "POST"));
   }

    // 實現其父類 AbstractAuthenticationProcessingFilter 提供的鉤子方法 用去嘗試認證
   public Authentication attemptAuthentication(HttpServletRequest request,
         HttpServletResponse response) throws AuthenticationException {
       // 判斷請求方式是不是POST
      if (postOnly && !request.getMethod().equals("POST")) {
         throw new AuthenticationServiceException(
               "Authentication method not supported: " + request.getMethod());
      }
      
       // 先去 HttpServletRequest 對象中獲取帳號名、密碼
      String username = obtainUsername(request);
      String password = obtainPassword(request);

      if (username == null) {
         username = "";
      }

      if (password == null) {
         password = "";
      }

      username = username.trim();

       // 而後把帳號名、密碼封裝到 一個認證Token對象中,這是就是一個通行證,可是這時的狀態時不可信的,一旦經過認證就變爲可信的
      UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
            username, password);

      // 會將 HttpServletRequest 中的一些細節 request.getRemoteAddr()   request.getSession 存入的到Token中
      setDetails(request, authRequest);

       // 而後 使用 父類中的 AuthenticationManager 對Token 進行認證 
      return this.getAuthenticationManager().authenticate(authRequest);
   }
   // 獲取密碼 很重要 若是你想改變獲取密碼的方式要麼在此處重寫,要麼經過自定義一個前置的過濾器保證能此處能get到
   @Nullable
   protected String obtainPassword(HttpServletRequest request) {
      return request.getParameter(passwordParameter);
   }

      // 獲取帳戶很重要 若是你想改變獲取密碼的方式要麼在此處重寫,要麼經過自定義一個前置的過濾器保證能此處能get到
   @Nullable
   protected String obtainUsername(HttpServletRequest request) {
      return request.getParameter(usernameParameter);
   }

   // 參見上面對應的說明爲憑據設置一些請求細節
   protected void setDetails(HttpServletRequest request,
         UsernamePasswordAuthenticationToken authRequest) {
      authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
   }

   // 設置帳戶參數的key
   public void setUsernameParameter(String usernameParameter) {
      Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
      this.usernameParameter = usernameParameter;
   }

   // 設置密碼參數的key
   public void setPasswordParameter(String passwordParameter) {
      Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
      this.passwordParameter = passwordParameter;
   }

   // 認證的請求方式是隻支持POST請求
   public void setPostOnly(boolean postOnly) {
      this.postOnly = postOnly;
   }

   public final String getUsernameParameter() {
      return usernameParameter;
   }

   public final String getPasswordParameter() {
      return passwordParameter;
   }
}

爲了增強對流程的理解,我特地畫了一張圖來對這個流程進行清晰的說明:框架

UsernamePasswordAuthenticationFilter工做流程

3. 咱們能夠定製什麼

根據上面的流程,咱們理解了UsernamePasswordAuthenticationFilter工做流程後能夠作這些事情:源碼分析

  • 定製咱們的登陸請求URI和請求方式。
  • 登陸請求參數的格式定製化,好比可使用JSON格式提交甚至幾種並存。
  • 如何將用戶名和密碼封裝入憑據UsernamePasswordAuthenticationToken,定製業務場景須要的特殊憑據。

4. 咱們會有什麼疑問

AuthenticationManager從哪兒來,它又是什麼,它是如何對憑據進行認證的,認證成功的後續細節是什麼,認證失敗的後續細節是什麼。不要走開,持續關注:碼農小胖哥 爲你揭曉這個答案。post

關注公衆號:Felordcn 獲取更多資訊ui

我的博客:https://felord.cnthis

相關文章
相關標籤/搜索