目录
- 1. Spring Security 5.6
- 2. Spring Security OAuth2
- 3. OAuth2、OIDC1.0的典型场景
- 4. 关于Spring Authorization Server
- 5. Spring Authorization Server扩展实现
- 6. 后续...
最近由于工作需要,目前正对此开源库spring-authorization-server进行调研及扩展,故作此文章记录相关历程。
1. Spring Security 5.6
起初接触Spring Security,总感觉抓不到重点,不知道从哪入手,所以给人一种不够轻量的感觉。想要用好Spring Security,还是需要对其架构有一定深入研究的,不然遇到问题都不知道从哪入手。先简单说说Spring Security的总体架构。
如上图SpringSecurity是建立在Servlet Filter的基础上,
Spring Security通过DelegatingFilterProxy
连接Servlet Filter和Spring上下文,
核心的SpringSecurity过滤器执行逻辑可参见FilterChainProxy
,
FilterChainProxy
会根据配置维护多条过滤器链SecurityFilterChain
,
而单条过滤器链SecurityFilterChain
由多个Filter
组成,每个Filter
都会各自执行具体逻辑。
DelegatingFilterProxy - 桥接Servlet Filter和Spring上下文
- FilterChainProxy - 包含SecurityFilterChain(Security过滤器链)且根据path匹配不同的过滤器链
- SecurityFilterChain - 包含多个SecurityFilter(Security过滤器)
- SecurityFilter - 具体的过滤器实现(GenericFilterBean)
FilterChainProxy
通过调用SecurityFilterChain.matches
方法来决定执行哪条过滤器链SecurityFilterChain
,
可以理解为根据RequestMatcher
来区分执行哪条链,仅执行第一次匹配成功的过滤器链。
注:默认不配置RequestMatcher则对所有请求都会执行过滤器链
我们可以通过debug断点,来观察当前Spring Security应用有多少条过滤器链,以及各个过滤器的执行顺序。
我通常都会在FilterChainProxy类中的getFilters方法中如图位置设置断点。
在断点处,即可查看当前SecuritySecurity共有多少条过滤器链,以及每条过滤器链中包含的过滤器,
如此以来更有助于清楚的了解Security的执行过程及问题排查。
说了这么多的Filter,那Spring Security是怎么设置这些Filter的呢?
比如一段典型的Spring Security配置:
@EnableWebSecurity
public class DefaultSecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated()
)
//表单登录
.formLogin(withDefaults());
//OAuth2相关
//.oauth2Login(...)
//.oauth2Client(...)
//.oauth2ResourceServer(...)
return http.build();
}
}
如上代码中,可以点进formLogin方法,可以发现每个配置入口都有一个对应的Configurer对象,如FormLoginConfigurer extends SecurityConfigurerAdapter,可以进入具体Configure对象的configure方法进行查看,便可发现具体的filter及其相关设置。
具体单个Filter的实现可查看源码,
比如认证相关的Filter,其总体结构如下:
AuthenticationProcessingFilter - 认证过滤器
- AuthenticationManager -> ProviderManager - 认证管理器
- AuthenticationProvider - 具体的认证逻辑实现
- AuthenticationSuccessHandler - 认证成功处理器
- AuthenticationFailureHandler - 认证失败处理器
注:
关于SpringSecurity不再讲更多了,想要了解更多可参见我之前的文章:SpringSecurity5.6架构杂记
2. Spring Security OAuth2
Spring Seurity在2019年11月发布了OAuth 2.0 Migration Guide,
- 宣布禁用原spring-security-oauth,并将其合并到Spring Security 5.2+,
- 并且不再提供Authorization Server的支持。
后续在广大开发者的呼声下,在2020年4月Spring官方发布声明Announcing the Spring Authorization Server,
- 宣布启动由社区主导的spring-authorization-server项目,专注于OAuth2 Authorization Server实现,
- 目前最新版本为spring-authorization-server:0.2.2(发布于2022-01-27)。
截止到今天(2022-03-13):
如上几大模块组成了Spring Security对OAuth2生态的最新支持,
本文使用的OAuth2相关模块也是基于此最新生态。
3. OAuth2、OIDC1.0的典型场景
之前看的好多OAuth2流程示例,都是基于Web应用(即包含Web后端),而现如今普遍采用前后端分离的架构,即Web后端提供API,前端采用SPA实现(如Vue等),即前端作为Client端,后端作为Resource Server端。而SPA Client端暴露在用户浏览器端(用户可以通过浏览器查看SPA代码),因此无法在SPA Client端安全的存储client_secret。
注:
关于OAuth2及OIDC的相关术语、端点、选型等可参见我之前的文章:《OIDC(及Oauth)选型建议及SSO、SLO方案》
故基于SPA场景
,推荐使用如下模式:
- 授权码Code流程 + PKCE
- Authorization Code Flow with Proof Key for Code Exchange (PKCE)
- PKCE使用code_challenge及code_verifier代替原client_secret验证,避免暴露client_secret
- 支持Refresh Token流程并支持Refresh Token轮换
- 提升用户体验,无需反复登录
- 提升安全性,即每次通过refresh_token换取新token后,作废旧的refresh_token并生成且和返回新的refresh_token
所以此次对spring-authorization-server的使用及扩展也是重点关注此SPA场景
。
同时结合OIDC
相关登出协议,期望扩展支持单点登录SSO、单点登出SLO
的场景。
协议 | 功能 |
RP端向OP发出的初始登出请求(适用于单点登出SLO, OP首先清除OP端的session, 然后向当前session下的其他已登录的RP触发Front-Channel或者Back-Channel登出请求, 最终可通过 | |
返回OP前端登出页面, 页面通过嵌入iframe(src= 同时触发当前OP Session对应的多个Client的登出接口 | |
OP直接向当前OP Session对应的多个Client后端服务(不依赖User Agent,如不依赖浏览器)发送登出请求 且适用于User Agent被关闭的时候也可以退出登录。 | |
RP端通过 收到登录状态改变通知后RP应该清除自己的Session信息、页面跳转等。 |
查看Spring Security相关源码,可以发现如上提到的核心功能支持情况如下表:
注:
由于本文重点在讲解Authorization Server后端服务,并未对SPA端代码进行集成与示例,
暂以Spring Security OAuth2 Login模块模仿SPA端协议执行过程,
关于SPA端可以找寻对应的OAuth/OIDC客户端库实现。
功能 | Authorization Server | OAuth2 Client |
Authorization Code Flow with PKCE 授权码Code流程 + PKCE | ✔️ | ✔️ |
Refresh Token without client_secret PKCE后续无client_secret刷新token | ❌ | ❌ |
Refresh Token Retotation(Reuse) 令牌轮换 | ✔️ | ✔️ |
OAuth2 SSO 单点登录 | ✔️ | ✔️ |
OIDC end_session_endpoint 单点登出 | ❌ | ✔️ |
所以以上表格中未实现的,也就是需要后续进行扩展的功能。
4. 关于Spring Authorization Server
Spring Authorization Server的核心协议支持可参见:
https://github.com/spring-projects/spring-authorization-server/wiki/Feature-List#completed-features
目前已提供的Filter及说明如下表:
核心FIlter | endpoint | 说明 |
OAuth2AuthorizationEndpointFilter | GET|POST /oauth2/authorize | 授权端点,即RP跳转到OP的认证入口, 且EU认证通过后,OP重定向回RP,且附加code参数 |
OAuth2ClientAuthenticationFilter | POST /oauth2/token|introspect|revoke | 即RP向OP发送获取token请求、检查token、吊销token时,OP端提供的认证逻辑 |
OAuth2TokenEndpointFilter | POST /oauth2/token | Token端点,RP向OP请求Token(通过code换token、执行refresh_token流程) |
OAuth2TokenIntrospectionEndpointFilter | POST /oauth2/introspect | 校验Token端点,RP请求OP检测token有效性 |
OAuth2TokenRevocationEndpointFilter | POST /oauth2/revoke | 吊销Token端点,RP请求OP吊销token |
OidcProviderConfigurationEndpointFilter | GET /.well-known/openid-configuration | OIDC协议发现端点 |
OidcUserInfoEndpointFilter | GET /userinfo | 用户信息端点,提供用户信息查询 |
OidcClientRegistrationEndpointFilter | POST /connect/register | 客户端信息注册端点(暂未使用) |
注:
关于Spring Authorization Server核心FIlter及其执行逻辑说明,
可参见我之前的文章:《SpringSecurity5.6架构杂记 - OAuth2/Authorization Server》
5. Spring Authorization Server扩展实现
关于Spring Authorization Server的扩展实现,
我已经放到了我的开源代码库中了:https://gitee.com/luoex/spring-cloud-demo/tree/develop/spring-security-demo
模块 | 说明 |
OIDC AuthServer核心功能Base模块,其他示例模块均依赖此模块 | |
OIDC AuthServer最小集成示例 | |
OIDC AuthServer扩展token示例 | |
OIDC AuthServer同时作为Resource Server示例 | |
OIDC AuthServer自定义登录图片验证码集成示例 | |
OIDC AuthServer集成第三方OAuth2(GitHub)登录示例 | |
OIDC AuthServer共享session集成示例(支持AuthServer分布式部署) | |
OIDC AuthServer综合集成示例(自定义登录页、手机验证码登录、token扩展、第三方登录、同时作为资源服务器) |
oauth2-auth-server-oidc 的核心扩展点包括:
- 支持自定义token(id_token、access_token)
- 支持自定义userInfo
- 支持集成第三方OAuth2登录(如GitHub)及第三方用户注册
- 完善PKCE及refresh_token验证
- 扩展支持OIDC SLO(end_session_endpoint)
- 自定义通用登录模型(使用Map<String, String> authParam代替原唯一username参数)
- 支持自定义登录页(支持登录表单Ajax提交)、授权确认页
针对之前提到的SPA典型场景,结合此次扩展给出oauth2-auth-server-oidc 认证及刷新Token的时序图如下:
放几张集成示例的效果图:
默认登录页:
默认授权确认页面:
自定义图片验证码登录页:
自定义组合登录页示例(支持第三方GitHub登录):
自定义组合登录页 - 手机验证码登录示例:
6. 后续…
关于oauth2-auth-server-oidc 对spring-authorization-server的扩展实现会持续完善,有时间也会将扩展思路写到博客上。