文章目录
前言
在开始之前务必下载好源码 spring-security-oauth2-2.3.x版本https://codeload.github.com/spring-projects/spring-security-oauth/zip/2.3.x,
并基于 spring-security-oauth2 配置(二),将注解配置开始分析源码称为A路线 ,将授权模式访问分析源码称为B路线
A路线与B路线互相印证。
@EnableAuthorizationServer
最开始的注解当然是这两个注解@EnableAuthorizationServer、@EnableResourceServer,AuthorizationServer是授权服务,那么先从@EnableAuthorizationServer开始。发现类上使用@Import导入了两个配置类,那么先从AuthorizationServerSecurityConfiguration配置类开始
AuthorizationServerSecurityConfiguration
AuthorizationServerSecurityConfiguration:授权服务器安全配置类
AuthorizationServerSecurityConfiguration配置类上又使用了@Import导入了两个配置类,并且继承了spring-security包中的抽象类WebSecurityConfigurerAdapter,先分析configure(HttpSecurity http)方法。
AuthorizationServerSecurityConfigurer 对象的暴露
@Override
protected void configure(HttpSecurity http) throws Exception {
// 这个类之前出现在我们授权服务器配置类中,因为我们的案例的DemoAuthorizationServerConfiguration继承了AuthorizationServerConfigurerAdapter,
// 重写了它的方法configure(AuthorizationServerSecurityConfigurer security),
// AuthorizationServerSecurityConfigurer根据源码类的注释,它的作用是:配置授权服务器端点的安全
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
// 接下来看到了FrameworkEndpointHandlerMapping,根据源码类注释,它的描述:框架端点处理器。其实我们可以将它做成一个Controller。
// 这个对象是由AuthorizationServerEndpointsConfiguration对象中获取的,AuthorizationServerEndpointsConfiguration之后再分析
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
// 将FrameworkEndpointHandlerMapping保存到HttpSecurity对象中一个hashMap中,用于后面共享使用
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
// 将AuthorizationServerSecurityConfigurer对象暴露给外部使用
configure(configurer);
// 应用AuthorizationServerSecurityConfigurer配置,如果被外部修改了配置
http.apply(configurer);
// 根据参数名称,猜测获取了FrameworkEndpointHandlerMapping中配置的:token端点、token_key、token检查端点的访问路径,具体之后查看
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
// 是否使用了自定义的UserDetailsService,如果不是自定义,将其设置到AuthorizationServerEndpointsConfigurer中
if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
// 将获取的token端点、token_key、token检查端点的访问路径分别配置访问权限,并将ClientDetailsService的实现类
// 保存到HttpSecurity对象中一个hashMap中,用于后面共享使用
// @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);
}
ClientDetailsServiceConfigurer对象的暴露
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
// 将ClientDetailsServiceConfigurer对象通过AuthorizationServerConfigurer接口暴露给外部使用
for (AuthorizationServerConfigurer configurer : configurers) {
configurer.configure(clientDetails);
}
}
ClientDetailsServiceConfiguration
ClientDetailsServiceConfiguration:客户端服务配置类
@Configuration
public class ClientDetailsServiceConfiguration {
// 使用基于构造模式的ClientDetailsServiceBuilder创建了ClientDetailsServiceConfigurer
@SuppressWarnings("rawtypes")
private ClientDetailsServiceConfigurer configurer = new ClientDetailsServiceConfigurer(new ClientDetailsServiceBuilder());
@Bean
public ClientDetailsServiceConfigurer clientDetailsServiceConfigurer() {
return configurer;
}
// 使用基于构造模式的ClientDetailsServiceConfigurer创建了ClientDetailsService
@Bean
@Lazy
@Scope(proxyMode=ScopedProxyMode.INTERFACES)
public ClientDetailsService clientDetailsService() throws Exception {
return configurer.and().build();
}
}
ClientDetailsServiceConfigurer
ClientDetailsServiceConfigurer使用构造模式,提供了灵活配置方式,这种设计思想在spring-security、spring-security-oauth2中经常大量出现。
// 1、提供直接设置ClientDetailsService接口实现类
public ClientDetailsServiceBuilder<?> withClientDetails(ClientDetailsService clientDetailsService) throws Exception {
setBuilder(getBuilder().clients(clientDetailsService));
return this.and();
}
// 2、提供构造器设置内存类型ClientDetailsService
public InMemoryClientDetailsServiceBuilder inMemory() throws Exception {
InMemoryClientDetailsServiceBuilder next = getBuilder().inMemory();
setBuilder(next);
return next;
}
// 3、提供构造器设置数据源类型ClientDetailsService
public JdbcClientDetailsServiceBuilder jdbc(DataSource dataSource) throws Exception {
JdbcClientDetailsServiceBuilder next = getBuilder().jdbc().dataSource(dataSource);
setBuilder(next);
return next;
}
AuthorizationServerEndpointsConfiguration
AuthorizationServerEndpointsConfiguration:授权服务器端点配置类
AuthorizationServerEndpointsConfiguration类上使用了@Import导入了TokenKeyEndpointRegistrar,TokenKeyEndpointRegistrar之后再分析。下面只暂时重要部分的分析:
// 通过@PostConstruct注解在应用初始化的时候调用init方法
@PostConstruct
public void init() {
for (AuthorizationServerConfigurer configurer : configurers) {
try {
// 使用AuthorizationServerConfigurer接口暴露AuthorizationServerEndpointsConfigurer对象
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());
authorizationEndpoint.setRedirectResolver(redirectResolver());
return authorizationEndpoint;
}
// 定义了token访问端点
@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;
}
// 定义了token检查端点
@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
endpoint.setExceptionTranslator(exceptionTranslator());
return endpoint;
}
AuthorizationServerEndpointsConfigurer
AuthorizationServerEndpointsConfigurer:可以配置端点相关的配置项(TokenGranter、TokenEnhancer、AuthenticationManager、UserDetailsService、TokenServices等等)
在AuthorizationServerEndpointsConfiguration中使用AuthorizationServerConfigurer接口暴露AuthorizationServerEndpointsConfigurer对象。
AuthorizationServerEndpointsConfigurer中端点相关的配置项都已经有默认的配置
TokenGranter
TokenGranter:token授予者,实际是根据grantType与TokenRequest去调用合适的TokenGranter实现类获取OAuth2Authentication,在AbstractTokenGranter中去调用authenticationManager认证authentication信息,起到一个承上启下的作用。
CompositeTokenGranter
TokenGranter接口的实现类CompositeTokenGranter,遍历其内部所有的TokenGranter接口实现类,调用其具体实现类。
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
// 遍历所有的TokenGranter接口实现类,判断是否支持该grantType然后进行授权
for (TokenGranter granter : tokenGranters) {
OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
if (grant!=null) {
return grant;
}
}
return null;
}
在AuthorizationServerEndpointsConfigurer源码中使用代理模式调用了CompositeTokenGranter
private TokenGranter tokenGranter() {
if (tokenGranter == null) {
tokenGranter = new TokenGranter() {
private CompositeTokenGranter delegate;
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (delegate == null) {
delegate = new CompositeTokenGranter(getDefaultTokenGranters());
}
return delegate.grant(grantType, tokenRequest);
}
};
}
return tokenGranter;
}
在AuthorizationServerEndpointsConfigurer源码getDefaultTokenGranters()方法中,初始化了几种默认的TokenGranter的实现类:
- AuthorizationCodeTokenGranter
- RefreshTokenGranter
- ImplicitTokenGranter
- ClientCredentialsTokenGranter
- ResourceOwnerPasswordTokenGranter
private List<TokenGranter> getDefaultTokenGranters() {
ClientDetailsService clientDetails = clientDetailsService();
AuthorizationServerTokenServices tokenServices = tokenServices();
AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
OAuth2RequestFactory requestFactory = requestFactory();
List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,
requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
tokenGranters.add(implicit);
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
if (authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
clientDetails, requestFactory));
}
return tokenGranters;
}
AbstractTokenGranter
TokenGranter 接口的抽象类,使用模板方法设计模式,TokenGranter 接口的具体实现类都会继承该抽象类,在ClientDetails信息认证通过后调用TokenGranter 具体实现类的getOAuth2Authentication方法
AuthorizationCodeTokenGranter
适用于授权类型grantType为authorization_code授权码模式,使用AuthorizationCodeServices对该授权类型的ClientDetails和TokenRequest处理
RefreshTokenGranter
适用于授权类型grantType为refresh_token刷新模式,使用AuthorizationServerTokenServices对该授权类型的ClientDetails和TokenRequest处理
ImplicitTokenGranter
适用于授权类型grantType为implicit简化模式,是authorization_code授权码模式的简化形式,对于已认证用户才进行处理
ClientCredentialsTokenGranter
适用于授权类型grantType为client_credentials客户端凭证模式,直接使用AbstractTokenGranter中的实现,判断配置是否能刷新才返回
ResourceOwnerPasswordTokenGranter
适用于授权类型grant_type为password密码模式,使用AuthenticationManager认证请求
AuthorizationCodeServices
AuthorizationCodeServices接口提供两个接口方法:
为指定的身份验证创建授权码
String createAuthorizationCode(OAuth2Authentication authentication)
使用授权码获取用户身份信息
OAuth2Authentication consumeAuthorizationCode(String code)
RandomValueAuthorizationCodeServices
AuthorizationCodeServices接口的抽象类,生成了一个随机的字符串的code,AuthorizationCodeServices接口的具体实现类都继承RandomValueAuthorizationCodeServices,实现其抽象方法来保存身份信息与code以及通过code获取身份信息时候移除code两功能
InMemoryAuthorizationCodeServices
使用ConcurrentHashMap内存形式保存key为code,value为OAuth2Authentication
JdbcAuthorizationCodeServices
使用JdbcTemplate数据库表形式保存code与OAuth2Authentication
AuthorizationServerTokenServices
授权服务器TokenServices,主要提供3个接口方法:
// 使用身份信息创建token
OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;
// 刷新token
OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
throws AuthenticationException;
// 使用身份信息获取token
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
总结
本篇中从外部配置类逐步逆推分析,由于篇幅过长,本篇到此结束,下一篇继续分析源码。