Spring Cloud Security核心过滤器UsernamePasswordAuthenticationFilter解析

举报
chongz-z 发表于 2021/01/20 01:37:26 2021/01/20
【摘要】 在前面几篇文章介绍的三个过滤器总是会被spring-security-web配置到安全过滤器调用链中。但是直到此时,SecurtiyContext中都可能是空的,并没有真正的认证过程,因此过滤器调用链中还需要一个对用户身份进行认证的过滤器,允许用户进行身份认证,并为SecurityContext注入有效的用户信息。UsernamePasswordAuthenticationFilter就是其...

在前面几篇文章介绍的三个过滤器总是会被spring-security-web配置到安全过滤器调用链中。

但是直到此时,SecurtiyContext中都可能是空的,并没有真正的认证过程,因此过滤器调用链中还需要一个对用户身份进行认证的过滤器,允许用户进行身份认证,并为SecurityContext注入有效的用户信息。UsernamePasswordAuthenticationFilter就是其中最常用的身份认证过滤器,spring-security在/login端点登录时要求通过username和password进行登录就是通过该过滤器对认证进行处理。

UsernamePasswordAuthenticationFilter继承于AbstractAuthenticationProcessingFilter,其过滤器的核心处理流程如下,位于AbstractAuthenticationProcessingFilter中:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  throws IOException, ServletException 
{
 HttpServletRequest request = (HttpServletRequest) req;
 HttpServletResponse response = (HttpServletResponse) res;
 // 如果不需要认证,直接返回
 if (!requiresAuthentication(request, response)) {
  chain.doFilter(request, response);
  return;
 }
 Authentication authResult;
 try {
  authResult = attemptAuthentication(request, response);
  if (authResult == null) {
   // return immediately as subclass has indicated that it hasn't completed
   // authentication
   return;
  }
  sessionStrategy.onAuthentication(authResult, request, response);
 }catch (InternalAuthenticationServiceException failed) {
  // 认证失败
  unsuccessfulAuthentication(request, response, failed);
  return;
 }catch (AuthenticationException failed) {
  // Authentication failed
  unsuccessfulAuthentication(request, response, failed);
  return;
 }

 // 认证成功
 if (continueChainBeforeSuccessfulAuthentication) {
  chain.doFilter(request, response);
 }

 successfulAuthentication(request, response, chain, authResult);
 }

在这个流程中,AbstractAuthenticationProcessingFilter将真正进行认证的过程委托给子类的attemptAuthentication()方法进行,同时定义了unsuccessfulAuthentication()successfulAuthentication()方法对认证失败和认证成功进行处理,我们首先看一下这两个方法的实现。

protected void unsuccessfulAuthentication(HttpServletRequest request,
  HttpServletResponse response, AuthenticationException failed)

  throws IOException, ServletException 
{
 // 清理SecurityContext
 SecurityContextHolder.clearContext();
 rememberMeServices.loginFail(request, response);
 failureHandler.onAuthenticationFailure(request, response, failed);
}

protected void successfulAuthentication(HttpServletRequest request,
   HttpServletResponse response, FilterChain chain, Authentication authResult)

  throws IOException, ServletException 
{

 // 将认证成功返回的authResult保存到SecurtiyContext
 SecurityContextHolder.getContext().setAuthentication(authResult);
 rememberMeServices.loginSuccess(request, response, authResult);

 // Fire event
 if (this.eventPublisher != null) {
  eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
    authResult, this.getClass()));
 }
 successHandler.onAuthenticationSuccess(request, response, authResult);
}


在认证失败后,spring-security会对SecurityContext进行清理,并委托AuthenticationFailureHandler进行具体的处理,默认是返回401状态码或者重定向到认证失败页面;而认证成功后就会将认证后返回的Authentication保存到SecurityContext中,然后委托AuthenticationSuccessHandler进行处理,默认也是重定向到相关的登录成功页面或者继续进行请求。

UsernamePasswordAuthenticationFilter中的attemptAuthentication()主要是通过委托AuthenticationManager进行认证的,代码如下:

public Authentication attemptAuthentication(HttpServletRequest request,
  HttpServletResponse response)
 throws AuthenticationException 
{
 if (postOnly && !request.getMethod().equals("POST")) {
  throw new AuthenticationServiceException(
    "Authentication method not supported: " + request.getMethod());
 }
 // 从request中获取用户名和密码
 String username = obtainUsername(request);
 String password = obtainPassword(request);
 if (username == null) {
  username = "";
 }
 if (password == null) {
  password = "";
 }
 username = username.trim();
 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
   username, password);
 // 允许子类自定义额外的数据
 setDetails(request, authRequest);
 // 调用`AuthenticationManager`进行认证
 return this.getAuthenticationManager().authenticate(authRequest);
}

在该方法中,首先从request中获取到username和password,封装成UsernamePasswordAuthenticationToken用于交予AuthenticationManager进行真正的认证,总体来说流程还是很清晰的。认证成功后会返回充满丰富用户信息的UsernamePasswordAuthenticationToken用于接下来的请求。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。