Spring Security OAuth2 源码分析2 - TokenGranter

2,951 阅读3分钟

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) 这个过程可以理解为:

  1. 根据 client、tokenRequest 从 OAuth2RequestFactory 中创建一个 OAuth2Request, 进而可得到 OAuth2Authentication (存放着用户的认证信息)。
  2. 通过 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