Практичная галантерея Spring Security: иллюстрация того, как пользователи входят в систему

Spring Boot Java

1. Введение

Добро пожаловать в серию статей о практических галантерейных товарах Spring Security, посвященных интеграцииSpring SecurityКогда используется структура безопасности, первое, с чем мы можем столкнуться, — это настроить регистрацию и вход в систему в соответствии с фактическими потребностями нашего проекта, особенноHttpАутентификация входа. Согласно предыдущим статьям по теме,HttpАутентификация входа по фильтруUsernamePasswordAuthenticationFilterдля обработки. Мы можем сделать некоторую настройку, только если разберемся с этим фильтром. Сегодня мы просто проанализируем его исходный код и рабочий процесс.

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Откуда он берется, что это такое, как он аутентифицирует учетные данные, каковы дополнительные сведения об успешной аутентификации и каковы последующие данные об ошибке аутентификации. Не уходи, будь в курсе:Код Фермер Маленький Толстый БратОткрой для себя ответ.

关注公众号:Felordcn获取更多资讯

Личный блог: https://felord.cn