文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Security的登陆流程是什么

2023-06-25 12:50

关注

本篇内容介绍了“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);}

在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;}

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

@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {   //获取Authentication的运行时类   Class<? extends Authentication> 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的登陆流程是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯