在我们自定义的OAuth2的配置类 configure方法中,向 AuthorizationServerEndpointsConfigurer端点配置类添加了一些信息,有的是 AuthorizationServerEndpointsConfigurer中默认的信息。 比如:AuthorizationServerEndpointsConfigurer类中有一个 tokenServices属性。
AuthorizationServerTokenServices 接口
定义了一些我们可以对令牌进行一些必要的管理的操作方法。令牌被用来加载身份信息,里面包含了这个令牌的相关权限。
DefaultTokenServices类是AuthorizationServerTokenServices的唯一默认实现类。
DefaultTokenServices类是AuthorizationServerTokenServices的唯一默认实现类。
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
ConsumerTokenServices, InitializingBean {
// 刷新令牌的过期时间,默认30天
private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
// 访问令牌的过期时间,默认12小时
private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
// 是否支持刷新令牌
private boolean supportRefreshToken = false;
// 重用刷新令牌
private boolean reuseRefreshToken = true;
// 令牌存储
private TokenStore tokenStore;
private ClientDetailsService clientDetailsService;
// 令牌增强器
private TokenEnhancer accessTokenEnhancer;
// 认证管理器
private AuthenticationManager authenticationManager;
...
1、TokenStore令牌存储
TokenStore是 OAuth2 令牌的持久性接口,定义了存储及获取令牌的相关方法。
实现类有个下面几个: 默认使用的是InMemoryTokenStore来存储,如果用数据库,那么每次token服务查询、存储,都需要SQL操作。这里重点查看 JdbcTokenStore。
查看 DefaultTokenServices类的 createAccessToken方法。
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
// 1. tokenStore中获取令牌
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
if (existingAccessToken != null) {
// 2. 获取令牌有,但是过期了,则移除访问令牌及刷新令牌
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
// The token store could remove the refresh token when the
// access token is removed, but we want to
// be sure...
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}
else {
// 3.有令牌未过期,重新存储访问令牌以防身份验证发生变化
// Re-store the access token in case the authentication has changed
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
// Only create a new refresh token if there wasn't an existing one
// associated with an expired access token.
// Clients might be holding existing refresh tokens, so we re-use it in
// the case that the old access token
// expired.
// 4. 刷新令牌获取访问令牌为空,则创建
if (refreshToken == null) {
refreshToken = createRefreshToken(authentication);
}
// But the refresh token itself might need to be re-issued if it has
// expired.
else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = createRefreshToken(authentication);
}
}
// 5. 创建并存储令牌
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// In case it was modified
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}
查看 DefaultTokenServices类的 refreshAccessToken方法。
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
// 当前client不支持刷新,抛出InvalidGrantException
if (!supportRefreshToken) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
// 1.获取刷新令牌,
OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
// 2.使用刷新令牌,返回新的访问令牌
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
if (this.authenticationManager != null && !authentication.isClientOnly()) {
// The client has already been authenticated, but the user authentication might be old now, so give it a
// chance to re-authenticate.
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
String clientId = authentication.getOAuth2Request().getClientId();
if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
}
// clear out any access tokens already associated with the refresh
// token.
//3.移除刷新令牌
tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
if (isExpired(refreshToken)) {
tokenStore.removeRefreshToken(refreshToken);
throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
}
authentication = createRefreshedAuthentication(authentication, tokenRequest);
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshToken);
refreshToken = createRefreshToken(authentication);
}
/ 4. 创建并存储令牌
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
1.3 获取令牌的认证信息
查看 DefaultTokenServices类的 getAccessToken方法。 返回的是 OAuth2AccessToken对象。
OAuth2AccessToken接口定义了 OAuth2令牌的相关结构和属性。
public interface OAuth2AccessToken {
// 携带令牌访问的前缀
public static String BEARER_TYPE = "Bearer";
// OAuth2类型
public static String OAUTH2_TYPE = "OAuth2";
/**
* 授权服务器颁发的访问令牌名,该值是必需的
* The access token issued by the authorization server. This value is REQUIRED.
*/
public static String ACCESS_TOKEN = "access_token";
/**发行的令牌类型,值不区分大小写。该值是必需的
* The type of the token issued as described in Section 7.1. Value is case insensitive.
* This value is REQUIRED.
*/
public static String TOKEN_TYPE = "token_type";
/**
* 访问令牌的生命周期(以秒为单位),例如,值“3600”表示访问令牌将在生成响应后的一小时内到期。该值是可选的。
*/
public static String EXPIRES_IN = "expires_in";
/**
* 刷新令牌,该值是可选的
*/
public static String REFRESH_TOKEN = "refresh_token";
/**
* 访问令牌的范围
*/
public static String SCOPE = "scope";
/**
* 令牌序列化程序使用 additionalInformation 映射来导出 OAuth 扩展使用的任何字段
*/
Map getAdditionalInformation();
Set getScope();
OAuth2RefreshToken getRefreshToken();
String getTokenType();
boolean isExpired();
Date getExpiration();
int getExpiresIn();
String getValue();
}
DefaultOAuth2AccessToken类是它的默认的实现类。
DefaultOAuth2AccessToken是 OAuth2AccessToken接口的默认实现类。
public class DefaultOAuth2AccessToken implements Serializable, OAuth2AccessToken {
private static final long serialVersionUID = 914967629530462926L;
// 令牌值
private String value;
// 到期时间
private Date expiration;
// 令牌类型
private String tokenType = BEARER_TYPE.toLowerCase();
// 刷新令牌
private OAuth2RefreshToken refreshToken;
// 范围
private Set scope;
// 序列化字段
private Map additionalInformation = Collections.emptyMap();
// 根据提供的值创建访问令牌
public DefaultOAuth2AccessToken(String value) {
this.value = value;
}
// 查询过期的便捷方法
public boolean isExpired() {
return expiration != null && expiration.before(new Date());
}
public DefaultOAuth2AccessToken(OAuth2AccessToken accessToken) {
this(accessToken.getValue());
setAdditionalInformation(accessToken.getAdditionalInformation());
setRefreshToken(accessToken.getRefreshToken());
setExpiration(accessToken.getExpiration());
setScope(accessToken.getScope());
setTokenType(accessToken.getTokenType());
}
// 将Map转为OAuth2AccessToken
public static OAuth2AccessToken valueOf(Map tokenParams) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(tokenParams.get(ACCESS_TOKEN));
if (tokenParams.containsKey(EXPIRES_IN)) {
long expiration = 0;
try {
expiration = Long.parseLong(String.valueOf(tokenParams.get(EXPIRES_IN)));
}
catch (NumberFormatException e) {
// fall through...
}
token.setExpiration(new Date(System.currentTimeMillis() + (expiration * 1000L)));
}
if (tokenParams.containsKey(REFRESH_TOKEN)) {
String refresh = tokenParams.get(REFRESH_TOKEN);
DefaultOAuth2RefreshToken refreshToken = new DefaultOAuth2RefreshToken(refresh);
token.setRefreshToken(refreshToken);
}
if (tokenParams.containsKey(SCOPE)) {
Set scope = new TreeSet();
for (StringTokenizer tokenizer = new StringTokenizer(tokenParams.get(SCOPE), " ,"); tokenizer
.hasMoreTokens();) {
scope.add(tokenizer.nextToken());
}
token.setScope(scope);
}
if (tokenParams.containsKey(TOKEN_TYPE)) {
token.setTokenType(tokenParams.get(TOKEN_TYPE));
}
return token;
}
...
}
通过查看源码,对OAuth2令牌的生成和它相关结构和属性信息有了一定认识。
– 求知若饥,虚心若愚。