引言
在现代应用中,授权码模式(Authorization Code Grant)是 OAuth 2.0(或2.1) 中最常用的授权方式之一。它通过安全的方式允许客户端应用程序代表用户访问资源。在 Spring Security 中,授权码模式的实现不仅复杂而且功能丰富。本文将通过底层源码探究,深入理解 Spring Security 授权码模式的工作原理。
本帖基于 Spring Security 6.2.0 版本进行深入探索。在这一版本中,Spring 官网将 Spring Security 中的授权服务器功能独立抽离,封装为一个新项目------Spring Authorization Server。两者之间的关系既紧密又相辅相成:Spring Security 提供了基础的安全机制,而 Spring Authorization Server 则在其之上实现了 OAuth 2.0 和 OpenID Connect 的授权功能。这种结合使得开发者能够构建出既安全又灵活的认证与授权解决方案,从而满足现代应用对安全性的高要求。
1.Spring Authorization Server
在 Spring Authorization Server 的设计与实现中,出于增强安全性的考虑,决定剔除原有的密码模式(Resource Owner Password Credentials Grant)和隐式模式(Implicit Grant),同时引入设备授权码模式(Device Authorization Grant)。所以高版本的Spring Security 目前仅支持以下几种授权模式:
-
授权码模式:本章节的核心介绍点,也是应用最广泛的
-
客户端模式:需要token时,手动调用授权服务器的oauth2/token,但不支持令牌的刷新
-
刷新令牌模式:通过refresh_token获取有效地token
-
设备授权码模式:解决设备(即客户端)需要访问某些受限的资源但又无法在设备上直接输入用户凭证的情况,比如智能电视、游戏机、IoT 设备、打印机等。感兴趣的小伙伴可以参考:深入解读 Spring Security 的设备授权码模式:底层运作原理-CSDN博客
2.Spring Security------Authorization Code Grant
这是最常用的授权模式,适用于服务器端应用。用户通过客户端应用重定向到授权服务器进行身份验证,成功后授权服务器返回一个授权码,客户端使用该授权码向授权服务器请求访问令牌,进而访问后端资源信息。
1.授权码工作流程
在探讨源码之前,首先让我们回顾一下授权码模式的基本流程:
- 用户重定向:用户在客户端应用中请求访问受保护的资源,应用将用户重定向到授权服务器。
- 用户认证:用户在授权服务器上进行身份验证,并同意授权。
- 获取授权码:授权服务器将用户重定向回客户端,并附加授权码。
- 交换令牌:客户端使用授权码向授权服务器请求访问令牌。
- 访问资源:客户端使用访问令牌访问受保护的资源。
上面的几步是整个流程的核心步骤,对于刚研究授权码模式的小伙伴来说可能比较抽象,那么接下来我们通过图的方式来展示整个流程,具体如下:
通过上图,整个授权码工作流程就比较清晰了,那么其底层原理又是如何呢?接下来我们就从源码的角度来分析授权码流程中关键的几个过滤器。
2.从源码角度探究授权码模式
众所周知Spring Security工作原理是基于一系列的过滤器来进行拦截处理的,我们可以通过自定义配置类中的SecurityFilterChain执行链来进行扩展处理。所以再不同的项目中,Spring Security的过滤器是不同的,因项目需要而定。至于如何自定义,自定义的SecurityFilterChain又是如何加载初始化,Web容器再接收到请求后又是如何到达Spring Security中的过滤器的,感兴趣的小伙伴可以参考:深入剖析Spring Security: 底层源码视角下的初始化流程-CSDN博客 。本章节就不再做过多介绍了。
言归正传,既然我们要从源码的角度深入研究授权码模式的工作原理,那么首先理清整体的请求流动逻辑显得尤为重要。只有掌握了这一全局视角,才能避免在分析过程中感到无从下手。具体流程如下图:
图片原始地址:授权码流程 流程图模板_ProcessOn思维导图、流程图
由上面的图片我们可以看到一系列的过滤器,图中对每个过滤器的作用也进行了简短的概述,小伙伴们只需要根据请求的流动顺序(图中的请求带有编号)进行阅读,即可从整体上掌握整个授权码流程。
整个图分成了两大块,授权服务项目和客户端项目。需要注意的是再Spring Authorization Server中,默认情况下会帮我们生成两条执行链,分别是登录执行链和授权执行链。其中授权执行链的优先级要比登录执行链高,当请求进入时会根据请求路径进行匹配,如果该路径不在授权执行链路径匹配器中则才会由登录执行链进行处理。而客户端执行链默认只有一条。接下来我们按照执行链来进行学习扩展。
1.登录执行链
为了确保项目的安全性,所有项目都必须进行登录处理。当我们在页面上输入用户名和密码并点击登录时,授权服务会拦截该请求,并对其进行认证处理。再该执行链中比较核心的过滤器UsernamePasswordAuthenticationFilter。
1.UsernamePasswordAuthenticationFilter
AbstractAuthenticationProcessingFilter的子类,对/doLogin请求进行拦截处理,认证处理器为DaoAuthenticationProvider,对请求中的用户名和密码进行校验。具体源码如下:
接着看一下UsernamePasswordAuthenticationFilter.attemptAuthentication方法,具体如下:
上图中的认证器为DaoAuthenticationProvider(父类AbstractUserDetailsAuthenticationProvider
),那么我们继续看一下:
2.授权执行链
主要用于授权、生成code、生成令牌、注册客户端、向数据库保存操作记录等功能,拦截固定的请求样式,再授权码模式下比较重要的核心类有:OAuth2AuthorizationEndpointFilter、OAuth2TokenEndpointFilter。
1.OAuth2AuthorizationEndpointFilter
用来处理客户端项目发来的/oauth2/authorize请求,生成code信息,并返回给客户端项目,其源码如下:
上图中的认证处理器主要有两个:OAuth2AuthorizationCodeRequestAuthenticationProvider和OAuth2AuthorizationConsentAuthenticationProvider。默认使用的是OAuth2AuthorizationCodeRequestAuthenticationProvider,接下来我们看一下该类,具体源码如下:
2.OAuth2TokenEndpointFilter
用来处理客户端项目发来的/oauth2/token请求,生成token信息,并返回给客户端项目,其源码如下:
授权码模式下,使用的认证处理器为OAuth2AuthorizationCodeAuthenticationProvider,其源码如下:
3.客户端执行链
客户端执行链的作用,就是对我们访问资源的请求进行拦截处理,判断是否已认证和权限是否充足,该执行链的核心类主要是OAuth2AuthorizationRequestRedirectFilter、OAuth2LoginAuthenticationFilter和ExceptionTranslationFilter。
1.ExceptionTranslationFilter
对AuthorizationFilter过滤器抛出的异常进行捕获处理,授权端捕获异常后一般是跳转登录页面,而客户端则是通过LoginUrlAuthenticationEntryPoint将当前请求重定向到当前项目的/oauth2/authorization/{registrationId}路径中。具体源码如下:
2.OAuth2AuthorizationRequestRedirectFilter
用来处理发来的/oauth2/authorization请求,进而重定向到授权服务器的/oauth2/
authorize请求上获取code,其源码如下:
3.OAuth2LoginAuthenticationFilter
AbstractAuthenticationProcessingFilter的子类,对授权服务器发来的/login/oauth2/code/请求进行拦截处理,拿着请求带来的code,然后通过远程调用的方式再去授权服务器获取token,进而再根据token获取更详细的用户信息,具体源码如下:
上图中的认证处理器默认为OidcAuthorizationCodeAuthenticationProvider,其具体源码如下:
上述过程即为授权码模式下,整个请求的流转过程。
如果您希望更深入地学习Spring Security源码,我强烈推荐您访问以下项目链接:https://gitee.com/chengyadong555/spring-security.git 。在这个项目中,您将发现对Spring Security源码的逐行分析,作者不仅提供了丰富的注释,还融入了自己独到的理解和见解。