Spring Security OAuth2.0密码模式请求达到/oauth/token前debug记录

96 阅读4分钟

介绍:

跟踪调试Spring Security OAuth2.0的密码模式获取token过程,了解记录获取token的大体流程,便于后续遇到问题debug调试。这里调试使用的spring security版本5.7.6是较低的版本,截图中也会看到很多类被标记@Deprecated,低版本熟悉了再升级高版本也算是一个升级打怪的过程吧。高版本的SAS授权源码和本文记录有差异,后续也会尝试使用新版的授权方式实践一下。另外本次调试的前提是搭建好了授权服务(这里不展开搭建过程),且本次调试请求没有经过网关,只有auth和admin服务。auth服务提供授权、admin服务提供查询数据库服务。本文跟踪调试的是请求达到/oauth/token方法前的过程

image.png

源码跟踪调试

请求参数

如下密码模式请求的参数:

image.png

如下是客户端认证信息,需要提前内置到sys_oauth_clients表中

image.png

image.png

开始调试

调试断点大家都会,难点就是往往不知道在哪里打断点,尤其是security、gatway这种黑盒型框架。如下截图是spring security官网架构文档(docs.spring.io/spring-secu… ,从文档描述上看,大概可以知道security的功能都是围绕着FilterChainProxy这个过滤器进行的。(如果你也去看了官方文档,记得翻到FilterChainProxy描述位置,前面描述的都是Servlet的FilterChain,不要打错了断点位置)。

image.png

FilterChainProxy

就把第一个断点打在FilterChainProxy的doFilter方法上。请求:http://localhost:9002/oauth/token接口 ,可以看到请求直接卡在了断点处。

image.png

一直走,进入到doFilterInternal()方法中,查看filter对象可以看到一共有12个filter,这些是FilterChainProxy默认代理的一些filter,当这些filter都通过后请求才会转发到后端的/oauth/token接口上。本次调试重点关注圈出来的3个filter。BasicAuthenticationFilter用于做客户端认证,ExceptionTranslationFilter用于处理认证过程中的异常处理,FilterSecurityInterceptor是FilterChainProxy链上最后一个filter,会将请求继续转发给后续的Filter(非securiy chain的filter),即上面截图中的Filter2。

image.png

BasicAuthenticationFilter

断点打到BasicAuthenticationFilter的doFilter中,F9进入。BasicAuthenticationFilter做的事情主要是圈出来的3处,不关注记住我等其他操作。

image.png

BasicAuthenticationFilter的convert()方法

convert的逻辑比较简单,就是把把我们设置的client信息提取出来。我们在Auth头中设置的Basic Auth参数username/password会被自动转换(base64编码)后自动存入到header中,格式为:Authorization:Basici XXXXXX。covert的逻辑就是按照这个约定的格式提取出对应的client_id和client_secret信息,并封装成 UsernamePasswordAuthenticationToken对象后返回。

image.png

image.png

BasicAuthenticationFilter的authenticate()方法

Authentication authResult = this.authenticationManager.authenticate(authRequest);

这行代码的授权业务逻辑流程很多,不做详细展开。大概得流程如下:

ProviderManager#authenticate()  ->  AbstractUserDetailsAuthenticationProvider#authenticate()#retrieveUser() -> DaoAuthenticationProvider#this.getUserDetailsService().loadUserByUsername(username);

this.getUserDetailsService()这个方法获取的对象就是在AuthorizationServerConfig配置类中配置的ClientDetailsService类,该类用于从数据库中读取请求客户端的信息。这里ClientDetailsService实现类使用admin提供的feign接口查询数据库。

image.png

ClientDetailsService实现类 image.png

另外从源码可以看到请求的客户端认证参数必须存在于库中,如果查询不到将会抛异常

image.png

ExceptionTranslationFilter

ExceptionTranslationFilter用于认证过程中的异常处理,尝试修改密码cicada为cicada1,发起请求可以看到捕获到了AccessDenied异常。

image.png

image.png

FilterSecurityInterceptor

FilterSecurityInteceptor的doFilter拦截请求后会调用invoke方法,真的逻辑在抽象父类里。请求会委托给AccessDecisionManager来决策是否能访问路径。

image.png

image.png

WebExpressionVoter的Support匹配FilterInvocation类型,因此最终是否通过访问的决策是由WebExpressionVoter投票出来的。

image.png

后续决策流程源码很长,不过跟了半天也未看到与如下配置匹配的期望代码,后续慢慢学习了。

image.png

FilterSecurityInterceptor它是FilterChainProxy链上最后一个Filter,可以认为是debug一个重要执行点,它执行往后就把请求转发给了FilterChainProxy链以外的后续filter,即开头官网给的截图中的Filter2处其他的过滤器。此时F9进入下一个断点后,请求就才正式达到我们自定义的Controller接口中。接下来就是熟悉的TokenEndpoint获取access_token流程,这里不展开介绍,会单独写一篇文章介绍其源码跟踪流程。

image.png