总文档 :文章目录
Github : github.com/black-ant
一 . 前言
这一篇我们继续深入 SpringSecurity , 看看其 OAuth2.0 的流程逻辑.
二 . 简易使用
2.1 Maven 依赖
1 | xml复制代码<dependency> |
2.2 配置项
1 | java复制代码@Configuration |
Resource 资源配置
1 | java复制代码@Configuration |
OAuthConfig 专属属性
1 | java复制代码@Configuration |
2.3 数据库
详见项目
2.4 使用方式
请求方式
1 | java复制代码http://localhost:8080/security/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=app |
AccessToken
1 | js复制代码var settings = { |
check Token
1 | java复制代码var settings = { |
三 . 源码解析
3.1 基础类
TokenStore
TokenStore 是一个接口 , 既然是一个接口 , 就意味着使用中是可以完全定制的
1 | java复制代码public interface TokenStore { |
我们再来看看 TokenStore 主要的实现类 , 默认提供了 以下几种实现 :
InMemoryTokenStore
- 从名字就能看到 , 该类是将 Token 放在内存中 ,
- 点进去就能看到 , 类中准备了大量的 ConcurrentHashMap , 保证多线程访问的安全性
1 | java复制代码// 整体其实没什么看的 , 唯一有点特殊的就是 , 里面不是一个集合 , 而是每个业务一个集合 , 这其实相当于分库分表的处理思路 |
JdbcTokenStore
JdbcTokenStore 是可行的处理方式 , 但是并不是最优解 , 数据库处理对高并发 , 高性能会带来不小的挑战
1 | JAVA复制代码// 关键点一 : SQL 写死了 , 点开就能看到 , sql 是定死的 , 但是提供了 Set 方法 , 即可定制 |
RedisTokenStore
Redis 存储 Token , 比较常见的存储方式 , 一般是首选方案 , 环境影响不能使用才会次选 JDBC
- 冒号区分文件夹
- RedisConnectionFactory 需要 redis 包
- JdkSerializationStrategy 序列化策略
- 序列化这一块反而是最应该关注的 , 部分监控框架可能会和序列化方式存在冲突
后面说一下它的另外2个特别的实现类 , 他们不是一种持久化的方式
JwkTokenStore
- 提供了对使用JSON Web密钥(JWK)验证JSON Web令牌(JWT)的JSON Web签名(JWS)的支持
- 令牌库实现专门用于资源服务器 , 唯一责任是解码JWT并使用相应的JWK验证其签名(JWS)
- 从这个介绍大概就知道了 , 他是用于资源服务器的 , 他的主要目的是转换 , 所以点开后不难发现 , 里面有一个对象用于底层调用
1 | java复制代码private final TokenStore delegate : 通过该对象再去处理底层的方式 |
扩展资料 :
- JWK RFC : tools.ietf.org/html/rfc751…
- JWT RFC : tools.ietf.org/html/rfc751…
- JWS RFC : tools.ietf.org/html/rfc751…
JwtTokenStore
这个对象其实是一个全新的体系 , 是 Token 的 JWT 实现 , 而不仅仅只是一种存储方式
1 | java复制代码- private JwtAccessTokenConverter jwtTokenEnhancer; |
- 可以看到提供了一个 转换的成员变量和一个 存储的 store 对象
- 这里要注意的是 , 他只从令牌本身读取数据。不是真正的存储,它从不持久化任何东西.
- 因为 , JWT 本身就存储了数据
3.2 事件类型和处理
事件用于推送 , 主要使用的有 DefaultAuthenticationEventPublisher ,我们来看看他
DefaultAuthenticationEventPublisher
从构造器里面可以看到大概的事件类型
1 | java复制代码public DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { |
3.3 Service 处理类
ResourceServerTokenServices 接口
1 | JAVA复制代码public interface ResourceServerTokenServices { |
DefaultTokenServices
默认的 Token 处理类 , 没看到特别的东西, 主要是对 TokenStore 的调用
RemoteTokenServices
- 查询/check_token端点以获取访问令牌的内容。如果端点返回400响应,这表明令牌无效。
1 | java复制代码C- RemoteTokenServices |
3.4 Token 管理体系
TokenGranter 是一个接口 , 他有很多实现类,
其中最常见的应该是 AuthorizationCodeTokenGranter 和 ImplicitTokenGranter , RefreshTokenGranter
1 | java复制代码C- AuthorizationCodeTokenGranter |
3.5 Token Conversion 体系
1 | java复制代码C- DefaultAccessTokenConverter |
3.6 配置类详情
OAuth 中除了原本的 User 概念 ,同时还有个 Client 概念 ,每个 Client 都可以看成一类待认证的对象 , **Spring OAuth 中提供了 OAuth 协议的自动配置 **, 主要包含2个类 :
- 实现 User 的认证
- AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter
- 实现 Resource 的认证
- ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered
1 | java复制代码 |
四 . 运行逻辑
看完了配置逻辑 , 我们来看一下主要的运行逻辑 :
4.1 请求入口
我们从请求的 入口 authorize 来开始 , 看看请求经过了哪些途径 , 来到了这里
流程开始
跳过一系列 Invoke 和 MVC 的流程 , 找到了 FilterChainProxy 类 , 大概就能知道 , OAuth 协议是在Filter 整体流程里面对请求进行的过滤
下面来找一下是哪个过滤类讲请求拦截到登录页的 :
Step 1 : SecurityContextPersistenceFilter
这里主要是执行 Filter 链后在 finally 中处理
Step 2 : BasicAuthenticationFilter
可以看到 , 代码中做了这些事情
1 | java复制代码C01- BasicAuthenticationFilter |
Filter 的逻辑其实之前就已经讲了 , 这里也就不太深入了
其他扩展 Filter
TokenEndpointAuthenticationFilter
TokenEndpoint的可选身份验证过滤器。它位于客户端的另一个过滤器(通常是BasicAuthenticationFilter)的下游,如果请求也包含用户凭证,它就会为Spring SecurityContext创建一个OAuth2Authentication .
如果使用这个过滤器,Spring安全上下文将包含一个OAuth2Authentication封装(作为授权请求)、进入过滤器的表单参数和来自已经经过身份验证的客户端身份验证的客户端id,以及从请求中提取并使用身份验证管理器验证的已验证用户令牌。
OAuth2AuthenticationProcessingFilter
针对OAuth2受保护资源的认证前过滤器。
从传入请求提取一个OAuth2令牌,并使用它用OAuth2Authentication(如果与OAuth2AuthenticationManager一起使用)填充Spring安全上下文。
4.2 接口详情
注意 , 到这个接口时候 ,认证其实已经完成了 , 拦截的过程详见上文 Filter , 这一部分只分析内部的流程
接口一 : authorize
1 | java复制代码http://localhost:8080/security/oauth/authorize |
接口二 : AccessToken 接口 /oauth/token
1 | java复制代码C07- TokenEndpoint |
接口三 : CheckTokenEndpoint - /oauth/check_token
1 | java复制代码C08- CheckTokenEndpoint |
Client 核心处理
Client 也是 OAuth 中一个非常核心的概念 , 毫无意外 , Client 的校验仍然是通过 Filter 处理的
1 | JAVA复制代码C- ClientCredentialsTokenEndpointFilter |
可以看到 , 这里完全是将 Client 当成一个用户在认证 , 整个体系都得到了通用
业务处理之 : OAuth2ErrorHandler 异常处理
TODO
AuthenticationProvider 体系结构
AuthenticationProvider 的整个体系结构异常庞大 , 他是认证的主体
TODO
4.3 其他重要工具类
DefaultStateKeyGenerator
默认state 构建工具
1 | java复制代码private RandomValueStringGenerator generator = new RandomValueStringGenerator(); |
4.4 异常类
- OAuth2AccessDeniedException
- 当访问被拒绝时,我们通常想要一个403,但是我们想要与所有其他OAuth2Exception类型一样的处理
- UserApprovalRequiredException
- 许可异常
- UserRedirectRequiredException
- 抛出该异常 , 许可令牌重定向
- AccessTokenRequiredException
- JwkException
4.5 补充类
OAuth2RestTemplate
OAuth2 的定制 RestTemplate 使用所提供资源的凭据发出oauth2认证的Rest请求
1 | java复制代码// 其中包含了一些和 OAuth 相关的定制 |
ProviderDiscoveryClient
看这代码 , OAuth2 应该还支持 OIDC 呢 , 该类用于发现 OIDC 规范配置的提供者的客户端
1 | java复制代码public ProviderConfiguration discover() { |
1 | java复制代码 |
4.6 业务的扩展定制
我们最终的目的是为了知道哪些节点可以扩展 :
Security 可以扩展的地方主要有这几类 :
- 使用接口的地方
- TokenStore
- ResourceServerTokenServices
- ClientDetailsService
- AuthorizationServerConfigurer
- 使用开关的地方
- 扩展 Filter , 监听 AccessToken
- OAuth2AuthenticationFailureEvent
- OAuth2ClientAuthenticationProcessingFilter
- 提供Set 方法的地方
TODO : 这个光说没用 , 后续会尝试做个 Demo 出来
总结
整体来说走了个大概 , 其中还有些零零散散的小点就不想走了 , 精力有限 , 后面如果涉及到 ,再完善到其中去
参考和感谢
本文转载自: 掘金