TokenEndPoint 获取令牌过程中, 有个这样的步骤:
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
TokenGranter, 字面上的理解: 令牌授予者。 以下是各授权模式对应的 TokenGranter:
实现类 | 对应的授权模式 |
---|---|
AuthorizationCodeTokenGranter | 授权码模式 |
ClientCredentialsTokenGranter | 客户端模式 |
ImplicitTokenGranter | implicit 模式 |
RefreshTokenGranter | 刷新 token 模式 |
ResourceOwnerPasswordTokenGranter | 密码模式 |
本文暂时只对 ResourceOwnerPasswordTokenGranter、RefreshTokenGranter 的源码作分析。
1. AbstractTokenGranter
以上实现类直接继承了抽象类 AbstractTokenGranter, 让我们通过源码来学习基本的 TokenGranter 授予令牌过程。
/**
* @author Dave Syer
*/
public abstract class AbstractTokenGranter implements TokenGranter {
// 核心部分代码...
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
// 判断传入的 grantType 是否符合当前的 TokenGranter。
if (!this.grantType.equals(grantType)) {
return null;
}
// 根据 TokenRequest 中的 clientId 加载 Client 信息
String clientId = tokenRequest.getClientId();
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
// 判断 Client 信息中的 authorizedGrantTypes (已授权的 grantType) 是否包含着传入的 grantType
validateGrantType(grantType, client);
logger.debug("Getting access token for: " + clientId);
// 通过 tokenService 创建 OAuth2AccessToken 并返回
// note: TokenRequest 里有调接口时传进的 parameters (包含授权根据, 例如 username, password 等),
// 根据 client 信息 + tokenRequest 最终可得到 OAuth2AccessToken
return getAccessToken(client, tokenRequest);
}
// ...
}
getAccessToken(client, tokenRequest) 这个过程可以理解为:
- 根据 client、tokenRequest 从 OAuth2RequestFactory 中创建一个 OAuth2Request, 进而可得到 OAuth2Authentication (存放着用户的认证信息)。
- 通过 tokenService 去创建 OAuth2AccessToken (存放着用户的 token信息、过期时间)。
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
}
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, null);
}
2. ResourceOwnerPasswordTokenGranter
以密码模式为例, 它是这样重写 getOAuth2Authentication 的:
/**
* @author Dave Syer
*/
public class ResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {
// ... 核心部分代码
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
// 不将密码放入 details 中, 防止密码泄露
parameters.remove("password");
// 组装用户密码模式的认证信息
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
// 通过 authenticationManager 认证已组装好的信息
userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException ase) {
// 过期、被锁定、不能用的时候抛出
throw new InvalidGrantException(ase.getMessage());
}
catch (BadCredentialsException e) {
// username/password 错误时抛出
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
// 从工厂创建 OAuth2Request
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
// ...
}
3. RefreshTokenGranter
我们拿到的 token 终会过期的, 对应于刷新 token模式的 RefreshTokenGranter 则负责获取新的 OAuth2AccessToken。
/**
* @author Dave Syer
*/
public class RefreshTokenGranter extends AbstractTokenGranter {
// 核心部分代码 ...
@Override
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
// 传入的参数需要有 refresh_token (DefaultOAuth2AccessToken 中有 refreshToken 字段)
String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
// 调用 tokenService 的刷新方法得到新的 OAuth2AccessToken
return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
}
// ...
}
4. CompositeTokenGranter
Spring security oauth2 还提供了一个 TokenGranter, 它可以是很多个 TokenGranter 的集合。
/**
* @author Dave Syer
*/
public class CompositeTokenGranter implements TokenGranter {
private final List<TokenGranter> tokenGranters;
public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
}
/**
* 从 TokenGranter 列表中挨个提出来获取准许证 (也就是我们要的 Token), 只要有个能获取到立即返回结果。
*/
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
for (TokenGranter granter : tokenGranters) {
OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
if (grant!=null) {
return grant;
}
}
return null;
}
/**
* 添加 TokenGranter
*/
public void addTokenGranter(TokenGranter tokenGranter) {
if (tokenGranter == null) {
throw new IllegalArgumentException("Token granter is null");
}
tokenGranters.add(tokenGranter);
}
}
该系列文章:
Spring Security OAuth2 源码分析1 - TokenEndpoint
Spring Security OAuth2 源码分析2 - TokenGranter
Spring Security OAuth2 源码分析3 - TokenServices
持续更新中...