1、AuthorizationServerConfig分析
(1)HttpSecurity http分析
Oauth2.1的入口AuthorizationServerConfig的authorizationServerSecurityFilterChain(HttpSecurity http),打断点
可以看到 ,启动时候注入的变量HttpSecurity http,http中的configures注入11个,sharedObjects注入3个,有什么用户后面再说。
(2)OAuth2AuthorizationServerConfigurer分析
继续往下走,到http.getConfigurer(OAuth2AuthorizationServerConfigurer.class),得到http中注入的OAuth2AuthorizationServerConfigurer,进入OAuth2AuthorizationServerConfigurer类可以看一看,这里面都是Oauth2.1支持用户自定义一些方法的入口。例如,
客户存储的库:
/**
* Sets the repository of registered clients.
*
* @param registeredClientRepository the repository of registered clients
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
*/
public OAuth2AuthorizationServerConfigurer registeredClientRepository(RegisteredClientRepository registeredClientRepository) {
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
return this;
}
认证服务方法,就是token的增删改查的服务
/**
* Sets the authorization service.
*
* @param authorizationService the authorization service
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
*/
public OAuth2AuthorizationServerConfigurer authorizationService(OAuth2AuthorizationService authorizationService) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
getBuilder().setSharedObject(OAuth2AuthorizationService.class, authorizationService);
return this;
}
自定义一些认证端点
/**
* Configures the OAuth 2.0 Token Endpoint.
*
* @param tokenEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenEndpointConfigurer}
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
*/
public OAuth2AuthorizationServerConfigurer tokenEndpoint(Customizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer) {
tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class));
return this;
}
剩下还有很多入口等等。。。
继续往下走,先跳过资源服务oauth2ResourceServer,后面在细说。就是http.build(),意思就是开始初始化
(3)HttpSecurity build()分析
进入抽象类AbstractSecurityBuilder的build()方法,并进入其实现类doBuild()方法
(4)init()分析
这里看到,其实就是对每一个configurers的初始化。这个configurers其实就是我们前面谈到的HttpSecurity中的configures(会有所增加),这里我们主要说Oauth2.1相关的,我们直接看OAuth2AuthorizationServerConfigurer的初始化
看第一行代码,我们进入OAuth2ConfigurerUtils的getAuthorizationServerSettings的方法看看里面的实现
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
首先,从httpSecurity的sharedObjects中获取,如果获取到了就返回获取到的,如果没有获取到通过getBean从上下文中获取,并存入sharedObjects。
从这里看出,初始化OAuth2AuthorizationServerConfigurer一般有两种方式,一种是通过OAuth2AuthorizationServerConfigurer开放的接口设置,一种是通过@Bean注入通过上下文初始化。
例如我们AuthorizationServerConfig类中的RegisteredClientRepository是通过@Bean注入并通过上下文初始化OAuth2AuthorizationServerConfigurer
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
//针对 Spring Authorization Server 最佳实践配置
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults())
; // Enable OpenID Connect 1.0
http
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
return http.build();
}
/**
* 注册客户信息
* 注册client信息
*
*/
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oidc-client")
.clientSecret("{noop}secret")
/**
* client_secret_basic: 将 clientId 和 clientSecret 通过 ‘:’ 号拼接,( clientId 和 clientSecret 都在上面配置中,)并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic 前缀(Basic后有一个空格)
* client_secret_post :clientId 和 clientSecret 放到表单去发送请求
*/
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://www.baidu.com")
.redirectUri("http://localhost:9001/login/oauth2/code/oidc-client")
.redirectUri("http://localhost:9001/api/login/welcome")
.postLogoutRedirectUri("http://127.0.0.1:8080/")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.scope("all")
// 设置 Client 需要页面审核授权
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(oidcClient);
}
如果想换成 OAuth2AuthorizationServerConfigurer开放的接口初始化,只需要设置下就可以了
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
//针对 Spring Authorization Server 最佳实践配置
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.registeredClientRepository(registeredClientRepository())
.oidc(Customizer.withDefaults())
; // Enable OpenID Connect 1.0
http
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
return http.build();
}
/**
* 注册客户信息
* 注册client信息
*
*/
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oidc-client")
.clientSecret("{noop}secret")
/**
* client_secret_basic: 将 clientId 和 clientSecret 通过 ‘:’ 号拼接,( clientId 和 clientSecret 都在上面配置中,)并使用 Base64 进行编码得到一串字符,再在前面加个 注意有个 Basic 前缀(Basic后有一个空格)
* client_secret_post :clientId 和 clientSecret 放到表单去发送请求
*/
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://www.baidu.com")
.redirectUri("http://localhost:9001/login/oauth2/code/oidc-client")
.redirectUri("http://localhost:9001/api/login/welcome")
.postLogoutRedirectUri("http://127.0.0.1:8080/")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.scope("all")
// 设置 Client 需要页面审核授权
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(oidcClient);
}
基本上,Oauth2.1初始化的方式都是这个样子。
继续往下走,走到下图位置
查看configurers的初始化是通过createConfigurers()方法一个一个new 出来的,这些就是Oauth2.1默认的一些功能,例如OAuth2ClientAuthenticationConfigurer是client的一些默认的功能配置,OAuth2TokenEndpointConfigurer则是默认的几种模式的配置
进入OAuth2ClientAuthenticationConfigurer的初始化,主要看List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
主要组装Client认证鉴权的Providers
同理,进入OAuth2TokenEndpointConfigurer,主要封装了默认几种模式的Providers
并通过httpSecurity.authenticationProvider加入
(3)configure()分析
同样的道理,configurers也是这样子去看和分析的,AbstractSecurityBuilder的build()方法,看其实现类doBuild()方法中的configure()
这里先看OAuth2AuthorizationServerConfigurer的configure()初始化,核心就是加了一些fileter去做相应拦截
我们再看OAuth2ResourceServerConfigurer,这里很核心的就是加入了 BearerTokenAuthenticationFilter,这个filter就是校验header中的token的过滤器。
我们再看OAuth2TokenEndpointConfigurer的configure(HttpSecurity httpSecurity),这里就是加入了默认的几种模式数据Converter
(5)总结
通过HttpSecurity的build(),初始化convert、filter、provider,搭建起来框架的链路。
2、ResourceConfig分析
该类东西不多,主要是定义需要鉴权或者放行的一些地址。
3、DaoAuthenticationProvider分析
忽然蹦出DaoAuthenticationProvider,可能有点迷茫,具体是怎么用的呢,后面通过uthorization_code模式实现流程中慢慢串联,先介绍下里面的几个方法。
additionalAuthenticationChecks方法,看源码,其实就是看下存储的密码和输入的密码是否一致,不一致就是密码出错。
retrieveUser方法,看源码就是说通过userDetailsService获取用户,在这里就是ResourceConfig中注入的UserDetailsService