spring-security-oauth2 源码分析A路线(一)

前言

在开始之前务必下载好源码 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的实现类:

  1. AuthorizationCodeTokenGranter
  2. RefreshTokenGranter
  3. ImplicitTokenGranter
  4. ClientCredentialsTokenGranter
  5. 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);

总结

本篇中从外部配置类逐步逆推分析,由于篇幅过长,本篇到此结束,下一篇继续分析源码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值