Spring Security OAuth 资源服务器认证浅析

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【1月更文挑战第18天】之前的几篇文章介绍了如何进行用户认证并向客户端分发 Token,以及 Token 是如何生成的,这篇分析一下,当客户端拿着 Token 去访问资源的时候,资源服务器是如何验证 Token 是否合法的。

之前的几篇文章介绍了 Spring Security OAuth 如何进行用户认证并向客户端分发 Token,以及 Token 是如何生成的,这篇分析一下,当客户端拿着 Token 去访问资源的时候,资源服务器是如何验证 Token 是否合法的。

以下内容需要你了解 OAuth 的原理,可以阅读这篇文章回顾一下。

OAuth2AuthenticationProcessingFilter

在 Spring Security OAuth 的官方文档当中,有这样一段说明:

The following filter is required to implement an OAuth 2.0 Resource Server:

  • The OAuth2AuthenticationProcessingFilter is used to load the Authentication for the request given an authenticated access token.

翻译过来就是说,在实现一个 OAuth 2.0 资源服务器的时候, 需要使用到 OAuth2AuthenticationProcessingFilter 过滤器,它负责执行 Token 的认证。

我们可以找到这个过滤器,进行分析。以下是它的 doFilter 方法:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
      ServletException {
   

   final boolean debug = logger.isDebugEnabled();
   final HttpServletRequest request = (HttpServletRequest) req;
   final HttpServletResponse response = (HttpServletResponse) res;

   try {
   

      Authentication authentication = tokenExtractor.extract(request);

      if (authentication == null) {
   
         if (stateless && isAuthenticated()) {
   
            if (debug) {
   
               logger.debug("Clearing security context.");
            }
            SecurityContextHolder.clearContext();
         }
         if (debug) {
   
            logger.debug("No token in request, will continue chain.");
         }
      }
      else {
   
         request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
         if (authentication instanceof AbstractAuthenticationToken) {
   
            AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
            needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
         }
         Authentication authResult = authenticationManager.authenticate(authentication);

         if (debug) {
   
            logger.debug("Authentication success: " + authResult);
         }

         eventPublisher.publishAuthenticationSuccess(authResult);
         SecurityContextHolder.getContext().setAuthentication(authResult);

      }
   }
   catch (OAuth2Exception failed) {
   
      SecurityContextHolder.clearContext();

      if (debug) {
   
         logger.debug("Authentication request failed: " + failed);
      }
      eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
            new PreAuthenticatedAuthenticationToken("access-token", "N/A"));

      authenticationEntryPoint.commence(request, response,
            new InsufficientAuthenticationException(failed.getMessage(), failed));

      return;
   }

   chain.doFilter(request, response);
}

这里面的重点代码如下:

  1. 通过 tokenExtractor.extract(request) 从 request 中获取到一个认证信息,通过进一步查看方法实现的源码,不难发现,这里返回的认证信息是一个 PreAuthenticatedAuthenticationToken 类型的,为认证的信息,只是将客户端请求时所带的 Token 进行了封装。
  2. 第二处比较关键的地方,就是 authenticationManager.authenticate(authentication) ,这里是真正进行认证的逻辑。

这里的 authenticationManager 类型是 OAuth2AuthenticationManager(至于为什么,之后的文章中会涉及到,这里暂时不讨论)。接下来我们分析这里真正的认证逻辑。

OAuth2AuthenticationManager

我们先把 authenticate 方法的源码贴出来:

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
   

   if (authentication == null) {
   
      throw new InvalidTokenException("Invalid token (token not found)");
   }
   String token = (String) authentication.getPrincipal();
   OAuth2Authentication auth = tokenServices.loadAuthentication(token);
   if (auth == null) {
   
      throw new InvalidTokenException("Invalid token: " + token);
   }

   Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
   if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
   
      throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
   }

   checkClientDetails(auth);

   if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
   
      OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
      // Guard against a cached copy of the same details
      if (!details.equals(auth.getDetails())) {
   
         // Preserve the authentication details from the one loaded by token services
         details.setDecodedDetails(auth.getDetails());
      }
   }
   auth.setDetails(authentication.getDetails());
   auth.setAuthenticated(true);
   return auth;

}

分析如下:

  • 代码第 6 行,获取到 Token。
  • 代码第 7 行,通过 tokenServices.loadAuthentication(token) 从 TokenService 中,根据 Token 获取认证信息,类型为 OAuth2Authentication
  • 之后会检测客户端的合法性,然后封装通过验证的认证信息并返回。
  • 最后,这个认证信息会返回到 OAuth2AuthenticationProcessingFilter 中。

总结

如果你看过这个系列之前的文章,可以发现一个规律,不管是使用 Spring Security 进行用户身份的认证,还是使用 Spring Security OAuth 在资源服务器进行 Token 合法性的认证,有一个共同发的逻辑就是:在一个 Filter 中进行认证信息的封装,然后将信息给到一个 AuthenticationManager 接口的实现类,在其中的 authenticate 执行真正的认证逻辑,然后将认证后的结果返回给 Filter,根据结果执行之后的逻辑,或者返回认证失败。希望这个总结对你学习 Spring Security 有所帮助。

目录
相关文章
|
5天前
|
安全 Java 数据安全/隐私保护
|
8天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
28 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
19天前
|
Java Linux
Springboot 解决linux服务器下获取不到项目Resources下资源
Springboot 解决linux服务器下获取不到项目Resources下资源
|
19天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
8天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
20 2
|
8天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
23 2
|
8天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
35 0
|
8天前
|
存储 安全 Java
第3章 Spring Security 的用户认证机制(2024 最新版)(下)
第3章 Spring Security 的用户认证机制(2024 最新版)
32 0
|
8天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
17 0
|
8天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
35 0