千家信息网

Spring Security的登陆流程是什么

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,本篇内容介绍了"Spring Security的登陆流程是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学
千家信息网最后更新 2025年01月18日Spring Security的登陆流程是什么

本篇内容介绍了"Spring Security的登陆流程是什么"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

在Spring Security中,认证授权都是通过过滤器来实现的。

当开始登陆的时候,有一个关键的过滤器UsernamePasswordAuthenticationFilter,该类继承抽象类AbstractAuthenticationProcessingFilter,在AbstractAuthenticationProcessingFilter里有一个doFilter方法,一切先从这里说起。

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)      throws IOException, ServletException {   if (!requiresAuthentication(request, response)) {      chain.doFilter(request, response);      return;   }   try {      Authentication authenticationResult = attemptAuthentication(request, response);      if (authenticationResult == null) {         // return immediately as subclass has indicated that it hasn't completed         return;      }      this.sessionStrategy.onAuthentication(authenticationResult, request, response);      // Authentication success      if (this.continueChainBeforeSuccessfulAuthentication) {         chain.doFilter(request, response);      }      successfulAuthentication(request, response, chain, authenticationResult);   }   catch (InternalAuthenticationServiceException failed) {      this.logger.error("An internal error occurred while trying to authenticate the user.", failed);      unsuccessfulAuthentication(request, response, failed);   }   catch (AuthenticationException ex) {      // Authentication failed      unsuccessfulAuthentication(request, response, ex);   }}

首先requiresAuthentication先判断是否尝试校验,通过后调用attemptAuthentication方法,这个方法也就是UsernamePasswordAuthenticationFilter 中的attemptAuthentication方法。

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)      throws AuthenticationException {   if (this.postOnly && !request.getMethod().equals("POST")) {      throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());   }   String username = obtainUsername(request);   username = (username != null) ? username : "";   username = username.trim();   String password = obtainPassword(request);   password = (password != null) ? password : "";   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);   // Allow subclasses to set the "details" property   setDetails(request, authRequest);   return this.getAuthenticationManager().authenticate(authRequest);}

1.在UsernamePasswordAuthenticationFilter 的attemptAuthentication方法中,先是验证请求的类型,是否是POST请求,如果不是的话,抛出异常。(PS:登陆肯定要用POST方法了)
2.然后拿到username和password。这里使用的是obtainUsername方法,也就是get方法。

@Nullableprotected String obtainPassword(HttpServletRequest request) {   return request.getParameter(this.passwordParameter);}@Nullableprotected String obtainUsername(HttpServletRequest request) {   return request.getParameter(this.usernameParameter);}

由此我们知道了Spring Security中是通过get方法来拿到参数,所以在进行前后端分离的时候是无法接受JSON数据,处理方法就是自定义一个Filter来继承UsernamePasswordAuthenticationFilter,重写attemptAuthentication方法,然后创建一个Filter实例写好登陆成功和失败的逻辑处理,在HttpSecurity参数的configure中通过addFilterAt来替换Spring Security官方提供的过滤器。
3.创建一个UsernamePasswordAuthenticationToken 实例。
4.设置Details,在这里关键的是在WebAuthenticationDetails类中记录了用户的remoteAddress和sessionId。

public WebAuthenticationDetails(HttpServletRequest request) {   this.remoteAddress = request.getRemoteAddr();   HttpSession session = request.getSession(false);   this.sessionId = (session != null) ? session.getId() : null;}

5.拿到一个AuthenticationManager通过authenticate方法进行校验,这里以实现类ProviderManager为例。

@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {   //获取Authentication的运行时类   Class toTest = authentication.getClass();   AuthenticationException lastException = null;   AuthenticationException parentException = null;   Authentication result = null;   Authentication parentResult = null;   int currentPosition = 0;   int size = this.providers.size();      for (AuthenticationProvider provider : getProviders()) {       //判断是否支持处理该类别的provider      if (!provider.supports(toTest)) {         continue;      }      if (logger.isTraceEnabled()) {         logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",               provider.getClass().getSimpleName(), ++currentPosition, size));      }      try {          //获取用户的信息         result = provider.authenticate(authentication);         if (result != null) {            copyDetails(authentication, result);            break;         }      }      catch (AccountStatusException | InternalAuthenticationServiceException ex) {         prepareException(ex, authentication);         // SEC-546: Avoid polling additional providers if auth failure is due to         // invalid account status         throw ex;      }      catch (AuthenticationException ex) {         lastException = ex;      }   }   //不支持的话跳出循环再次执行   if (result == null && this.parent != null) {      // Allow the parent to try.      try {         parentResult = this.parent.authenticate(authentication);         result = parentResult;      }      catch (ProviderNotFoundException ex) {         // ignore as we will throw below if no other exception occurred prior to         // calling parent and the parent         // may throw ProviderNotFound even though a provider in the child already         // handled the request      }      catch (AuthenticationException ex) {         parentException = ex;         lastException = ex;      }   }   if (result != null) {       //擦除用户的凭证 也就是密码      if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {         // Authentication is complete. Remove credentials and other secret data         // from authentication         ((CredentialsContainer) result).eraseCredentials();      }      // If the parent AuthenticationManager was attempted and successful then it      // will publish an AuthenticationSuccessEvent      // This check prevents a duplicate AuthenticationSuccessEvent if the parent      // AuthenticationManager already published it      if (parentResult == null) {          //公示登陆成功         this.eventPublisher.publishAuthenticationSuccess(result);      }      return result;   }   // Parent was null, or didn't authenticate (or throw an exception).   if (lastException == null) {      lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",            new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));   }   // If the parent AuthenticationManager was attempted and failed then it will   // publish an AbstractAuthenticationFailureEvent   // This check prevents a duplicate AbstractAuthenticationFailureEvent if the   // parent AuthenticationManager already published it   if (parentException == null) {      prepareException(lastException, authentication);   }   throw lastException;}

6.经过一系列校验,此时登陆校验基本完成,当验证通过后会执行doFilter中的successfulAuthentication方法,跳转到我们设置的登陆成功界面,验证失败会执行unsuccessfulAuthentication方法,跳转到我们设置的登陆失败界面。

"Spring Security的登陆流程是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0