oauth2之token生成过程源码解读

oauth2之token生成过程源码解读

Posted by yishuifengxiao on 2019-10-15

oauth2 中生成 token 的部分的源码在 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint这个路径下,它位于
spring-security-oauth2这个依赖包中,具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@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(
"There is no client authentication. Try adding an appropriate authentication filter.");
}

String clientId = getClientId(principal);
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)) {
// The scope was requested or determined during the authorization step
if (!tokenRequest.getScope().isEmpty()) {
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.<String> emptySet());
}
}

if (isRefreshTokenRequest(parameters)) {
// A refresh token has its own default scopes, so we should ignore any added by the factory here.
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}

OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}

return getResponse(token);

}

从源码中函数的入参可以看出,函数未接收HttpServletRequest之类的定义,因此无法在下游获取到更多的参数信息。

总的来说,生成 token 的步骤如下

  1. 判断传入参数里否有认证信息,若无认证信息则返回异常提示
  2. 从认证信息中获取到 clientId,并根据 clientId 获取到系统里设置的用户的授权信息
  3. 根据授权信息和传入的参数信息构造一个TokenRequest实例
  4. 比较认证信息与系统里设置的用户的授权信息是否匹配
  5. 依次校验设置的用户的授权信息里的信息是否符合要求
  6. 获取TokenGranter实例,根据授权类型和TokenRequest请求信息生成 token

在上面的第一步中我们就可以得知,获取 token 时需要传入一个认证信息,但是这个认证是在哪里传入的呢,追踪源码可以,在我们访问/oauth/token时,请求需要先经过一系列 spring security 的过滤器链才能执行到这里。而在过滤器链中有一个名为 BasicAuthenticationFilter的过滤器,它负责从请求头中获取到 basic 参数信息并验证,在验证通过后会将认证信息放入到SecurityContextHolder中,故而这里能够获取到Principal信息。

在第二步中,通过 clientId 获取到系统里设置的用户的授权信息这一过程主要由ClientDetailsService来完成,它的接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
public interface ClientDetailsService {

/**
* Load a client by the client id. This method must not return null.
*
* @param clientId The client id.
* @return The client details (never null).
* @throws ClientRegistrationException If the client account is locked, expired, disabled, or invalid for any other reason.
*/
ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException;

}

最后,在真正获取 token 的过程中,由getTokenGranter()返回的CompositeTokenGranter负责整个处理过程,CompositeTokenGranter源码如下,它应用了代理模式,获取了系统中所有配置的TokenGranter,然后根据传入的grantType调用不同类型的TokenGranter实例来产生 token。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CompositeTokenGranter implements TokenGranter {

private final List<TokenGranter> tokenGranters;

public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
}

public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
for (TokenGranter granter : tokenGranters) {
OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
if (grant!=null) {
return grant;
}
}
return null;
}

public void addTokenGranter(TokenGranter tokenGranter) {
if (tokenGranter == null) {
throw new IllegalArgumentException("Token granter is null");
}
tokenGranters.add(tokenGranter);
}

}

根据下图可以发现,TokenGranter的依赖关系如下

image

在上面的tokenGranters中注入的TokenGranter实例其实是AbstractTokenGranter的五个子类的实例,它们分别负责四种类型的 token 的生成和更新。