本文主要研究Spring Security Oauth2 token获取流程,token的获取方式有5种,其中授权模式最复杂,故本文以授权模式为基准研究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信息
@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;
}
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;
}
因为获取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);
}
}