从源码看oauth2的token创建过程

oauth2的token生成工程源码解读。

工作流程图示例:

oauth2代码流程图

看完源码图之后,再看内部构造,一个一个说。

TokenEndpoint:

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
55
56
57
58
59
60
61
62
//#1.处理/oauth/token请求
@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.");
}
//获取clientId
String clientId = getClientId(principal);
//获取第三方应用的详细配置信息
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
//使用第三方应用信息创建TokenRequest
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
//有没有传clientId
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) {
//检查scope
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
//grant_type是否存在值,对应四种授权模式和刷新token
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");
//如果是授权码模式scope设置为空,根据获取code时的scope设置
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.
//设置scope
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}
//获取OAuth2AccessToken
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}

return getResponse(token);

}

ClientDetails

上述代码可以看出在获取token时,接收的clientId通过这段代码:getClientDetailsService().loadClientByClientId(clientId); 会得到一个ClientDetails,我们继续看loadClientByClientId方法:

1
2
3
4
5
6
7
8
9
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
// 通过clientDetailsStore根据clientId为key来进行获取
ClientDetails details = (ClientDetails)this.clientDetailsStore.get(clientId);
if (details == null) {
throw new NoSuchClientException("No client with requested id: " + clientId);
} else {
return details;
}
}

看一下clientDetailsStore是如何定义的:

1
private Map<String, ClientDetails> clientDetailsStore = new HashMap();

可以看到是一个map来进行存储其认识或者说协议好的客户端clientId和密码,那这个ClientDetails可以看做是一个bean,根据接收到的clientId获取的对应实体信息,大概可以这样理解,可能有误,但我暂时先这么看。

oauth2代码流程图

TokenRequest

那么TokenRequest又是什么呢?聪明的你应该已经猜到了,TokenRequest就可以看做是服务端生成token后的一个实例,就是上篇oauth2入门及介绍中的四种模式的请求实例,大概可以这么理解。上张图:

oauth2代码流程图

创建token的核心工作类

我们看TokenEndPoint这段代码:

1
OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

我们看看这个grant方法,这个方法是this.getTokenGranter(),也就是TokenGranter接口的一个方法,该方法实现介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class CompositeTokenGranter implements TokenGranter {
private final List<TokenGranter> tokenGranters;

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

public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
Iterator var3 = this.tokenGranters.iterator();

OAuth2AccessToken grant;
do {
if (!var3.hasNext()) {
return null;
}

TokenGranter granter = (TokenGranter)var3.next();
grant = granter.grant(grantType, tokenRequest);
} while(grant == null);

return grant;
}
// 其余方法略...
}

这里有点类似于建造者模式,先把TokenGranter创建出来,然后调用grant方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractTokenGranter implements TokenGranter {
// 省略其他方法...
private final AuthorizationServerTokenServices tokenServices;
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (!this.grantType.equals(grantType)) {
return null;
} else {
String clientId = tokenRequest.getClientId();
ClientDetails client = this.clientDetailsService.loadClientByClientId(clientId);
this.validateGrantType(grantType, client);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Getting access token for: " + clientId);
}

return this.getAccessToken(client, tokenRequest);
}
}

protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
return this.tokenServices.createAccessToken(this.getOAuth2Authentication(client, tokenRequest));
}

}

可以看到最终是由AuthorizationServerTokenServices来实现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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
// 先根据条件查看是否有当前的token
OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
if (existingAccessToken != null) {
// 判断是否超时
if (!existingAccessToken.isExpired()) {
// 没有超时继续使用
this.tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}

if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
this.tokenStore.removeRefreshToken(refreshToken);
}
// 如果超时则把token移除
this.tokenStore.removeAccessToken(existingAccessToken);
}

// refreshtoken刷新
if (refreshToken == null) {
refreshToken = this.createRefreshToken(authentication);
} else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken)refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = this.createRefreshToken(authentication);
}
}

// 没有获取到token,说明需要创建
OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken);
// 创建完成放入tokenStore,这个tokenStore就是我们配置类内配置的存储位置
this.tokenStore.storeAccessToken(accessToken, authentication);
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
this.tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}

由此,token创建完成。

写在最后

最近想做一套springboot的学习笔记,因为太菜,只能磊代码了,意在做一套开箱即用,开发具有参考价值的demo示例,方便以后参考:https://github.com/producted/spring-learning

#

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×