一、涉及到的类
EnableAuthorizationServer
AuthorizationServerEndpointsConfiguration
AuthorizationServerSecurityConfiguration
AuthorizationServerConfigurerAdapter
二、代码分析
1、授权服务器配置示例代码
我们先从使用代码说起看下授权服务器的几个部件,下面是我们上一节用例中的授权服务器配置代码
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置两个客户端,一个用于password认证一个用于client认证
clients.inMemory()
.withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("client_credentials")
.scopes("select")
.authorities("oauth2")
.secret("123456")
.and()
.withClient("client_2")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("oauth2")
.secret("123456");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(new RedisTokenStore(redisConnectionFactory))
.tokenStore(new InMemoryTokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
// 允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
endpoints.reuseRefreshTokens(true);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
}
@EnableAuthorizationServer 主要是启用授权服务器
然后我们继承了 AuthorizationServerConfigurerAdapter 适配器类,然后重写了里面的几个方法
下面我们一个个来看
@EnableAuthorizationServer
我们知道 spring boot 中大量采用了这种@Enabled插拔式设计,通过一个注解,将所需要的组件都给打包整理进来了,屏蔽了对外使用的复杂性
那么我们就来看下,这个注解里面都提供了哪些东西
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
先来熟悉一下oauth2 的包结构:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9cmT0kERNNzYE90MjRVTyplMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyAzM4UzM0cTMxMzMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
注解通过导入了2个类来完成授权服务所需要的所有配置
AuthorizationServerEndpointsConfiguration
AuthorizationServerSecurityConfiguration
AuthorizationServerEndpointsConfiguration
@Configuration
@Import(TokenKeyEndpointRegistrar.class)
public class AuthorizationServerEndpointsConfiguration {
private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@PostConstruct
public void init() {
for (AuthorizationServerConfigurer configurer : configurers) {
try {
configurer.configure(endpoints);
} catch (Exception e) {
throw new IllegalStateException("Cannot configure enpdoints", e);
}
}
endpoints.setClientDetailsService(clientDetailsService);
}
@Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(tokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
return authorizationEndpoint;
}
@Bean
public TokenEndpoint tokenEndpoint() throws Exception {
TokenEndpoint tokenEndpoint = new TokenEndpoint();
tokenEndpoint.setClientDetailsService(clientDetailsService);
tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
tokenEndpoint.setTokenGranter(tokenGranter());
tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
return tokenEndpoint;
}
@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
endpoint.setExceptionTranslator(exceptionTranslator());
return endpoint;
}
.....
@Bean
public AuthorizationServerTokenServices defaultAuthorizationServerTokenServices() {
return endpoints.getDefaultAuthorizationServerTokenServices();
}
.....
@Component
protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
JwtAccessTokenConverter.class, false, false);
if (names.length > 0) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
builder.addConstructorArgReference(names[0]);
registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
}
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
}
}
从这个类中,主要加载了哪些默认配置呢,核心的我已经放在上面了,其他的省略号代替细节暂时先不展开了,目前先看到这里,大致有个框架认识,知道各个组件是在哪里被组织起来的就可以了
- AuthorizationEndpoint 授权端点,这个很重要,这是授权的请求入口都在里面
- TokenEndpoint 主要是生成token逻辑都在里面
- CheckTokenEndpoint 校验token的逻辑
- AuthorizationServerTokenServices 授权服务token服务,这个很重要,TokenEndpoint 为接入层,那么这个就是服务层了,面向数据
- TokenKeyEndpointRegistrar token key 生成相关的一些组件聚合
AuthorizationServerSecurityConfiguration
@Configuration
@Order(0)
@Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(clientDetails);
}
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Over-riding to make sure this.disableLocalConfigureAuthenticationBldr = false
// This will ensure that when this configurer builds the AuthenticationManager it will not attempt
// to find another 'Global' AuthenticationManager in the ApplicationContext (if available),
// and set that as the parent of this 'Local' AuthenticationManager.
// This AuthenticationManager should only be wired up with an AuthenticationProvider
// composed of the ClientDetailsService (wired in this configuration) for authenticating 'clients' only.
}
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
configure(configurer);
http.apply(configurer);
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
// @formatter:off
http
.authorizeRequests()
.antMatchers(tokenEndpointPath).fullyAuthenticated()
.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
.and()
.requestMatchers()
.antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
// @formatter:on
http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}
protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(oauthServer);
}
}
}
这里面做的事情比较简单,主要就是打辅助。因为oauth2框架是基于 spring security 的,上面的逻辑主要是对用户请求进行一些权限设置。设置的都是AuthorizationServerEndpointsConfiguration 中配置的一些框架内置的授权端点的请求限制,像/oauth/token就是获取token的端点
2、AuthorizationServerConfigurerAdapter
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // 1
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 2
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // 3
}
}
《1处》 配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器
《2处》 配置OAuth2的客户端相关信息。client 端信息配置,可以是内存,也可以是数据库等,我们上面的例子中采用的InMemoryClientDetailsServiceBuilder 内存为数据源的方式存放client信息。
《3处》 配置AuthorizationServerEndpointsConfigurer众多相关类,包括配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory。主要是功能组件的指定,可以系统,也可以自定义
然而 AuthorizationServerConfigurerAdapter 本质是对 AuthorizationServerConfigurer 接口的实现。通过覆盖上面三个方法,进行自定义配置注入
《1处》方法的调用位置 AuthorizationServerSecurityConfiguration#configure(AuthorizationServerSecurityConfigurer)
protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(oauthServer);
}
}
《2处》方法的调用位置 AuthorizationServerSecurityConfiguration#configure(ClientDetailsServiceConfigurer)
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(clientDetails);
}
}
《3处》方法的调用位置 AuthorizationServerEndpointsConfiguration 构造器调用后进行的init初始化操作
@PostConstruct
public void init() {
for (AuthorizationServerConfigurer configurer : configurers) {
try {
configurer.configure(endpoints);
} catch (Exception e) {
throw new IllegalStateException("Cannot configure enpdoints", e);
}
}
endpoints.setClientDetailsService(clientDetailsService);
}
三、总结
至此,整个授权架构模式,已经很清楚的勾画出来了,后面我们对其中的几个核心组件进行讲解
参考:https://www.cnkirito.moe/Spring-Security-OAuth2-2/