在我们自定义的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<String, Object> getAdditionalInformation(); Set<String> 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<String> scope; // 序列化字段 private Map<String, Object> 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<String, String> 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<String> scope = new TreeSet<String>(); 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令牌的生成和它相关结构和属性信息有了一定认识。
– 求知若饥,虚心若愚。