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 有所帮助。

目录
相关文章
|
8天前
|
XML 安全 前端开发
Spring Security 重点解析(下)
Spring Security 重点解析
23 1
|
8天前
|
安全 Java 数据安全/隐私保护
|
8天前
|
弹性计算 前端开发 持续交付
云效产品使用常见问题之导入ECS主机到资源池找不到导入的入口如何解决
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
8天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
59 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
8天前
|
存储 安全 前端开发
第6章 Spring Security 的 Web 安全性(2024 最新版)(上)
第6章 Spring Security 的 Web 安全性(2024 最新版)
58 0
|
8天前
|
安全 Java Go
第6章 Spring Security 的 Web 安全性(2024 最新版)(下)
第6章 Spring Security 的 Web 安全性(2024 最新版)
64 1
|
8天前
|
安全 NoSQL Java
Spring Security 重点解析(上)
Spring Security 重点解析
23 1
|
1天前
|
存储 弹性计算 监控
【阿里云弹性计算】阿里云 ECS 性能优化秘籍:提升应用响应速度与资源利用率
【5月更文挑战第22天】阿里云ECS优化涉及实例规格选择、OS与应用配置、网络配置、存储优化及数据库连接池管理。合理挑选CPU和内存,关闭无关服务,利用EIP和负载均衡优化网络,选择合适存储类型,并通过监控工具进行性能分析和压力测试,以提升响应速度,优化资源利用率,降低成本,增强企业竞争力。示例展示了Java数据库连接池配置优化。通过持续探索和实践,可最大化发挥ECS潜力。
51 7
|
8天前
|
Java 开发者 Spring
Spring Boot中的资源文件属性配置
【4月更文挑战第28天】在Spring Boot应用程序中,配置文件是管理应用程序行为的重要组成部分。资源文件属性配置允许开发者在不重新编译代码的情况下,对应用程序进行灵活地配置和调整。本篇博客将介绍Spring Boot中资源文件属性配置的基本概念,并通过实际示例展示如何利用这一功能。
27 1
|
8天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
27 2