Spring Security Oauth2之获取token流程分析

前言

本文主要研究Spring Security Oauth2 token获取流程,token的获取方式有5种,其中授权模式最复杂,故本文以授权模式为基准研究token的获取,废话不多说,先上一张核心源码流程图!
>获取token路径
TokenEndpoint: 是入口controller,也就是我们请求/oauth/token返回token的接口

ClientDetailsService: 这个就有点类似SpringSecurity中的UserDetailsService,UserDetailsService是读取用户信息的,而
ClientDetailsService则是读取客户端信息的,也就是根据我们发送/oauth/token请求是存放在Authorization中的username和password,注意这个不是用户的,而是客户端的端点信息

ClientDetails: 这个就是用来存储ClientDetailsService查询到的客户端信息的

TokenRequest: 这也是用来封装请求中的一些其他信息如grant_type、client_id等,同时也会将ClientDetails放入到这里面

TokenGrande: 这个接口封装的就是SpringSecurityOAuth2提供的5中默认授权模式,这个接口中会根据传入的grant_type执行不同的授权逻辑,这里不管走那种授权模式,都会产生两个对象OAuth2Request和Authentication,最终这两个对象会组合为OAuth2Authentication

OAuth2Request: 这个就是将ClientDetails和TokenRequest的信息做一个整合

Authentication: 这个就是存储当前授权登录的用户信息,实际上也就是从UserDetailsService中得到的用户信息

OAuth2Authentication: 这个对象就是将当前授权登录的用户信息,当前授权的是那个客户端信息,还有授权模式是什么,还有一些授权中的其他参数,最终这些数据都会被封装在这个对象中

AuthorizationServerTokenServices: 这个接口实际上就是使用组装好的OAuth2Authentication按照TokenEnhance生成策略生成Token,按照TokenStore存储方式存储Token

OAuth2AccessToken: 这个就是最终返回去的Token信息

在这里插入图片描述

一、TokenEndpoint
1.1 TokenEndpoint属性
@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {

	private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();

	private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));

	private WebResponseExceptionTranslator<OAuth2Exception> providerExceptionHandler = new DefaultWebResponseExceptionTranslator();

	private TokenGranter tokenGranter;

	private ClientDetailsService clientDetailsService;

	private OAuth2RequestFactory oAuth2RequestFactory;

	private OAuth2RequestFactory defaultOAuth2RequestFactory;
}
1.2 TokenEndpoint的初始化

TokenEndpoint的初始化发生在配置类AuthorizationServerEndpointsConfiguration中,他是由注解@EnableAuthorizationServer导入的

@Configuration
@Import(TokenKeyEndpointRegistrar.class)
public class AuthorizationServerEndpointsConfiguration {

	private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();

	@Autowired
	private ClientDetailsService clientDetailsService;

	@Autowired
	private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();

	@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;
	}
1.3 postAccessToken

因为获取token的url是/oauth/token,所以他会进入下面的代码

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {

	private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();

	private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));


	@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
	public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        
        //接口必须进行了身份认证
		if (!(principal instanceof Authentication)) {throw new InsufficientAuthenticationException("");}
        
        //从请求头中取出clientId
		String clientId = getClientId(principal);
		//通过ClientDetailsService读取数据库端点信息
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
        //封装端点信息+请求其他参数
		TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

		if (clientId != null && !clientId.equals("")) {
			// Only validate the client details if a client authenticated during this
			// request.
			if (!clientId.equals(tokenRequest.getClientId())) {
				// double check to make sure that the client ID in the token request is the same as that in the
				// authenticated client
				throw new InvalidClientException("Given client ID does not match authenticated client");
			}
		}
		if (authenticatedClient != null) {
			oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
		}
		if (!StringUtils.hasText(tokenRequest.getGrantType())) {
			throw new InvalidRequestException("Missing grant type");
		}
		if (tokenRequest.getGrantType().equals("implicit")) {
			throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
		}

		if (isAuthCodeRequest(parameters)) {
			// 在授权步骤中请求或确定了范围
			if (!tokenRequest.getScope().isEmpty()) {
				tokenRequest.setScope(Collections.<String> emptySet());
			}
		}

		if (isRefreshTokenRequest(parameters)) {
			// 刷新令牌有其自己的默认作用域,因此我们应该忽略工厂在此处添加的任何标记。
			tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
		}
        //核心方法:获取token,并把它装配到OAuth2AccessToken中
		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}
		return getResponse(token);
	}
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security OAuth2 Authorization Server可以通过Token增强器(Token Enhancer)来为授权后返回的Access Token添加额外的信息。Token增强器是一个接口,它接收一个Access Token并返回一个增强后的Access Token。在Authorization Server配置类中,可以通过调用tokenEnhancer()方法来设置Token增强器,示例代码如下: ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private DataSource dataSource; @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123456"); return converter; } @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(tokenStore()) .accessTokenConverter(accessTokenConverter()) .tokenEnhancer(tokenEnhancer()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .allowFormAuthenticationForClients(); } } ``` 在上面的代码中,CustomTokenEnhancer是一个自定义的Token增强器,它可以在Access Token中添加额外的信息。示例代码如下: ```java public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("organization", authentication.getName() + "@test.com"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } } ``` 在上面的代码中,我们向Access Token中添加了一个名为“organization”的信息,它的值为当前用户的用户名加上@test.com。这种方式可以为Access Token添加任何我们需要的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值