2- 用户登录表单拦截 UsernamePasswordAuthenticationFilter

/*
 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.web.authentication;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

/**
 * Processes an authentication form submission. Called
 * {@code AuthenticationProcessingFilter} prior to Spring Security 3.0.
 * <p>
 * Login forms must present two parameters to this filter: a username and password. The
 * default parameter names to use are contained in the static fields
 * {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and
 * {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}. The parameter names can also be changed by
 * setting the {@code usernameParameter} and {@code passwordParameter} properties.
 * <p>
 * This filter by default responds to the URL {@code /login}.
 *
 * @author Ben Alex
 * @author Colin Sampaleanu
 * @author Luke Taylor
 * @since 3.0
 */

/**
 *  当用户登录的时候 当前的过滤器会被调用
 */
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

   //表单提交的 用户名
   public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";

   // 说明 如果用户想增强当前类的功能可以创建一个当前类的子类 在子类的构造方法中 修改SPRING_SECURITY_FORM_USERNAME_KEY和SPRING_SECURITY_FORM_PASSWORD_KEY
   // 并且增加到过滤器链中  使用 addfilterAt(new 子类, 当前类.class)
   //表单提交的 密码 必须以SPRING_SECURITY_FORM_PASSWORD_KEY 为主
   public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

   // 规定请求路径 以及请求方式
   private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
         "POST");

   // 用户名 传递参数解析的Key
   private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
   // 密码 传递参数解析的Key
   private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;

   // 哪一种方式的登录请求 - 仅限于POST
   private boolean postOnly = true;

   // 初始化
   public UsernamePasswordAuthenticationFilter() {
      super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
   }

   /**
    * 初始化
    * @param authenticationManager 认证管理器
    */
   public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
      super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
   }


   /*
      当用户调用 /login 时会被调用
      此方法为重新父类方法。 用户可以继承父类实现自定义的表单登录方法
    */
   @Override
   public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
         throws AuthenticationException {
      // 如果不是POST调用的话那么抛出 授权服务 异常
      if (this.postOnly && !request.getMethod().equals("POST")) {
         throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
      }
      // 解析获取username
      String username = obtainUsername(request);
      // 如果user 不为空的话 放入内存并且返回
      // 下次在其他用户调用的时候 直接返回而不是重新生成。 这里可能是考虑的用户ID可能会出现多次调用的时候对内存的优化
      username = (username != null) ? username.trim() : "";
      // 与上述但是直接返回
      String password = obtainPassword(request);
      password = (password != null) ? password : "";
      /**
       * UsernamePasswordAuthenticationToken 是在用户登录时 loadUserByUsername 返回的对象一致。用户可以在当前
       * 处理自己的登录逻辑
       * 由于此处我这里是 使用的oauth 所以在默认的登录中是未授权的 用户
       * 至于为什么这里写的这么详细呢 ? 是因为有些课程中老师的习惯是重写这个接口实现用户的登录
       * 有的老师是重写的loadUserByUsername 两者皆可 避免混淆
       */
      UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
            password);

      // Allow subclasses to set the "details" property
      setDetails(request, authRequest);
      //返回
      return this.getAuthenticationManager().authenticate(authRequest);
   }

   /**
    * Enables subclasses to override the composition of the password, such as by
    * including additional values and a separator.
    * <p>
    * This might be used for example if a postcode/zipcode was required in addition to
    * the password. A delimiter such as a pipe (|) should be used to separate the
    * password and extended value(s). The <code>AuthenticationDao</code> will need to
    * generate the expected password in a corresponding manner.
    * </p>
    * @param request so that request attributes can be retrieved
    * @return the password that will be presented in the <code>Authentication</code>
    * request token to the <code>AuthenticationManager</code>
    */

   // 从http请求获取password
   @Nullable
   protected String obtainPassword(HttpServletRequest request) {
      return request.getParameter(this.passwordParameter);
   }

   /**
    * Enables subclasses to override the composition of the username, such as by
    * including additional values and a separator.
    * @param request so that request attributes can be retrieved
    * @return the username that will be presented in the <code>Authentication</code>
    * request token to the <code>AuthenticationManager</code>
    */
   // 从http请求获取username
   @Nullable
   protected String obtainUsername(HttpServletRequest request) {
      return request.getParameter(this.usernameParameter);
   }

   /**
    * Provided so that subclasses may configure what is put into the authentication
    * request's details property.
    * @param request that an authentication request is being created for
    * @param authRequest the authentication request object that should have its details
    * set
    */
   //设置信息 子类可访问
   protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
      authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
   }

   /**
    * Sets the parameter name which will be used to obtain the username from the login
    * request.
    * @param usernameParameter the parameter name. Defaults to "username".
    */

   // 自定义登录的解析参数
   public void setUsernameParameter(String usernameParameter) {
      Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
      this.usernameParameter = usernameParameter;
   }

   /**
    * Sets the parameter name which will be used to obtain the password from the login
    * request..
    * @param passwordParameter the parameter name. Defaults to "password".
    */
   //设置密码的解析Parame 表单的name属性
   public void setPasswordParameter(String passwordParameter) {
      Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
      this.passwordParameter = passwordParameter;
   }

   /**
    * Defines whether only HTTP POST requests will be allowed by this filter. If set to
    * true, and an authentication request is received which is not a POST request, an
    * exception will be raised immediately and authentication will not be attempted. The
    * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
    * authentication.
    * <p>
    * Defaults to <tt>true</tt> but may be overridden by subclasses.
    */

   public void setPostOnly(boolean postOnly) {
      this.postOnly = postOnly;
   }

   public final String getUsernameParameter() {
      return this.usernameParameter;
   }

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

}

 

posted @ 2023-02-17 03:05  站在山顶的人  阅读(88)  评论(0编辑  收藏  举报