首页 > 经验记录 > 看下源码修下 SpringSecurityOAuth2 的bug,解决令牌检查端点未实现 OAuth2 规范带来的坑: ResourceServer introspect 错误

看下源码修下 SpringSecurityOAuth2 的bug,解决令牌检查端点未实现 OAuth2 规范带来的坑: ResourceServer introspect 错误

 

最近在自己搭一个使用 SpringSecutiryOAuth2 的认证服务器, 这里的接口基于 SpringMVC, 而资源服务器是 SpringCloudGateway 建立的网关层,实现是 WebFlux。

目的是为了在网关层做所有的鉴权操作。  其实一切都还好,ajax 登陆、OAuth2密码模式的 token 获取、token刷新等 都有序进行中。

 

认证的整个流程都没发现问题,可是一到鉴权的阶段就不对劲了。

主要就是令牌内省失败,正常的令牌没问题,但是令牌如果一过期/或者是错误的令牌, 直接就报错了。这我就觉得很不对劲。

问题是我想了想 无论是 AuthorizationServer 还是 ResourceServer , 我都没有对具体认证流程作出改动。 仅仅实现了 SpringSecurity 提供的拓展点。比如 Token存储、Client存储、token 的附加信息、权限查询 之类的。

那这就不应该啊?? 我这用的你默认的实现,怎么还能有问题呢?

 

 

而又由于网关层 也就是OAuth2 ResourceServer 他是一个 WebFlux 搭建的web服务,  这个东西调试是真的不好调,里面大量的 lambda 和异步看的我头都要炸了。

不过又还能怎么办呢? ResourceServer/ AuthorizationServer 源码看看看看他丫的。 Debug日志开他丫的。

可是看他这个 WebFlux 下的鉴权源码真的很痛苦。 所以我具体详细的 Debug 流程就不细说了。

 

 

ResourceServer introspect

首先就是看 ResourceServer 的令牌内省(introspect)  也就是检查令牌的机制流程

具体触发时机为:  一个客户端试图来请求 ResourceServer 受保护的资源时、若是携带了 Authorization 请求头( Bearer ) 时则会触发令牌内省

 

最终我找到了处理token 鉴权具体类,就是它:  NimbusReactiveOpaqueTokenIntrospector

这里贴一段 WebFlux 作为资源服务器处理 token 鉴权的流程源码。

Gateway的过滤器会提取出 Bearer Token 然后调用此方法。每个流程都写了注释, 还是很清晰的。

NimbusReactiveOpaqueTokenIntrospector 这个类里面所有的源码我都看了一边、并没有发现有什么问题。

只是对于异常处理我有点不满, 因为如果出现了异常,我作为请求资源服务的客户端看到的响应是一片空白的, 具体错误信息都放在了Response Header 里,这个我觉得不太好。到时候把他覆盖掉给他改了。

 

然后源码没看出什么花来,那就打断点看看,

结果在检查Http响应正确性的方法里也就是 adaptToNimbusResponse() 中发现了不对。

这是这个方法的源码

在断点时,请求完成了,结果判定走进了这个 if 块。也就是请求错误。不是200响应。

所以整个流程就到了这里中断。是这个响应的 HttpStatus 是400 (Bad request) 让我有点奇怪。 为什么会是400?

因为我看到后面的处理 validate() 效验返回值逻辑,正常来说请求应该是返回200,并且带上一个 active为false 的 Response body 才对啊。

 

AuthorizationServer CheckToken Endpoint

于是我立马就决定去看认证服务的 check_token 端点是怎么写的。

这是 SpringSecutiryOAuth2 默认的令牌检查端点的源码,   checkToken() 方法中我打了详细的注释

 

然后看到这里我就惊了。 他这里边的逻辑显示:  token 如果发现不对,或者是 token 正确但是过期了, 就直接抛一个异常。

然后看下面的异常处理(@ExceptionHandler 注解的方法) 内部的注释,这说的是人话么

因为这不是oauth资源,因此我们不想在此处发送未经授权的代码。”  我懂你的意思了, 知道你不想返回403状态造成资源服务器误解,

问题是你也不能够直接怼个 400 错误请求回去啊???

反复分析.jpg

先不说你返回啥400, 你光返回的不是200 就很有问题了。按照道理来说,这个接口只要进来了(即客户端身份验证已经通过了), 那么出去的响应肯定得是 200 才行

 

因为我看了OAuth2的文档,这是 OAuth2 令牌内省的规范。

https://www.oauth.com/oauth2-servers/token-introspection-endpoint/

 

里面很清楚的说到了,在下面这些情况下,都不应该返回错误响应,端点仅返回无效标志

  • 请求的令牌不存在或无效
  • 令牌已过期
  • 令牌已发出给与发出此请求的客户端不同的客户端

如果说出现了令牌过期,那么返回值应该是这样子的

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

 

 

 

问题解决方案

 

还能咋解决。 重写

这是我重写令牌检查端点后的代码:

 

重写令牌检查端点之后, 需要在认证服务器的 AuthorizationServerEndpointsConfigurer 配置中将端点映射路径修改, 即从原来的 /oauth/check_token  映射为自定义的端点路径。覆盖掉原先的实现。

 

 

 

最后的疑问

为啥你这 SpringSecurityOAuth2 WebFlux 检查令牌的流程是 按照规矩 来的  ??

为啥你这 SpringSecurityOAuth2 WebMVC 的检查令牌端点 不是按照规范实现 的??

 

你知道你这样搞, 资源服务和认证服务 接口对不上么??? 我佛了

 

           


EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00