【Spring Security】 Oauth2 授权服务 令牌管理
【Spring Security】 Oauth2 授权服务 令牌管理
Metadata
title: 【Spring Security】 Oauth2 授权服务 令牌管理
date: 2023-02-02 22:30
tags:
- 行动阶段/完成
- 主题场景/组件
- 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
- 细化主题/Module/SpringSecurity
categories:
- SpringSecurity
keywords:
- SpringSecurity
description: 【Spring Security】 Oauth2 授权服务 令牌管理
【Spring Security】 Oauth2 授权服务 令牌管理
OAuth2AccessToken
OAuth2AccessToken 接口定义了 OAuth2 令牌的相关结构和属性,他有一个默认的实现类 DefaultOAuth2AccessToken。
public interface OAuth2AccessToken {
// 携带令牌访问的前缀
public static String BEARER_TYPE = "Bearer";
// OAuth2类型
public static String OAUTH2_TYPE = "OAuth2";
// 授权服务器颁发的访问令牌名,该值是必需的
public static String ACCESS_TOKEN = "access_token";
// 发行的令牌类型,值不区分大小写。该值是必需的
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();
// 省略Getter setter
}
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());
}
// 将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;
}
AuthorizationServerTokenServices
在上篇文档中介绍到在 AuthorizationServerEndpointsConfigurer 端点配置类中,有一个 AuthorizationServerTokenServices 属性。
接口
AuthorizationServerTokenServices 接口定义了一些操作使得你可以对令牌进行一些必要的管理,令牌可以被用来 加载身份信息,里面包含了这个令牌的相关权限。
public interface AuthorizationServerTokenServices {
// 创建与指定凭据OAuth2Authentication关联的访问令牌
OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;
// 刷新访问令牌
OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
throws AuthenticationException;
// 提供的身份验证密钥存储的访问令牌(如果存在)
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
}
实现类
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;
// ClientDetailsService
private ClientDetailsService clientDetailsService;
// 令牌增强器
private TokenEnhancer accessTokenEnhancer;
// 认证管理器
private AuthenticationManager authenticationManager;
// 创建访问令牌
@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;
}
// 刷新访问令牌
@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);
}
// 获取刷新令牌,没有抛出InvalidGrantException
OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
// 使用刷新令牌,返回新的访问令牌
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.
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);
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return tokenStore.getAccessToken(authentication);
}
/**
* Create a refreshed authentication.
* 创建刷新的身份验证
* @param authentication The authentication.
* @param request The scope for the refreshed token.
* @return The refreshed authentication.
* @throws InvalidScopeException If the scope requested is invalid or wider than the original scope.
*/
private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) {
OAuth2Authentication narrowed = authentication;
Set<String> scope = request.getScope();
OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
if (scope != null && !scope.isEmpty()) {
Set<String> originalScope = clientAuth.getScope();
if (originalScope == null || !originalScope.containsAll(scope)) {
throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope
+ ".", originalScope);
}
else {
clientAuth = clientAuth.narrowScope(scope);
}
}
narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
return narrowed;
}
protected boolean isExpired(OAuth2RefreshToken refreshToken) {
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken;
return expiringToken.getExpiration() == null
|| System.currentTimeMillis() > expiringToken.getExpiration().getTime();
}
return false;
}
public OAuth2AccessToken readAccessToken(String accessToken) {
return tokenStore.readAccessToken(accessToken);
}
public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
InvalidTokenException {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
else if (accessToken.isExpired()) {
tokenStore.removeAccessToken(accessToken);
throw new InvalidTokenException("Access token expired: " + accessTokenValue);
}
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
if (result == null) {
// in case of race condition
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
if (clientDetailsService != null) {
String clientId = result.getOAuth2Request().getClientId();
try {
clientDetailsService.loadClientByClientId(clientId);
}
catch (ClientRegistrationException e) {
throw new InvalidTokenException("Client not valid: " + clientId, e);
}
}
return result;
}
public String getClientId(String tokenValue) {
OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
if (authentication == null) {
throw new InvalidTokenException("Invalid access token: " + tokenValue);
}
OAuth2Request clientAuth = authentication.getOAuth2Request();
if (clientAuth == null) {
throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
}
return clientAuth.getClientId();
}
public boolean revokeToken(String tokenValue) {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
if (accessToken == null) {
return false;
}
if (accessToken.getRefreshToken() != null) {
tokenStore.removeRefreshToken(accessToken.getRefreshToken());
}
tokenStore.removeAccessToken(accessToken);
return true;
}
private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
return null;
}
int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
String value = UUID.randomUUID().toString();
if (validitySeconds > 0) {
return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis()
+ (validitySeconds * 1000L)));
}
return new DefaultOAuth2RefreshToken(value);
}
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
if (validitySeconds > 0) {
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
}
token.setRefreshToken(refreshToken);
token.setScope(authentication.getOAuth2Request().getScope());
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}
/**
* The access token validity period in seconds
*
* @param clientAuth the current authorization request
* @return the access token validity period in seconds
*/
protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
if (clientDetailsService != null) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getAccessTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return accessTokenValiditySeconds;
}
/**
* The refresh token validity period in seconds
*
* @param clientAuth the current authorization request
* @return the refresh token validity period in seconds
*/
protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) {
if (clientDetailsService != null) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getRefreshTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return refreshTokenValiditySeconds;
}
/**
* Is a refresh token supported for this client (or the global setting if
* {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not set.
*
* @param clientAuth the current authorization request
* @return boolean to indicate if refresh token is supported
*/
protected boolean isSupportRefreshToken(OAuth2Request clientAuth) {
if (clientDetailsService != null) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
return client.getAuthorizedGrantTypes().contains("refresh_token");
}
return this.supportRefreshToken;
}
/**
* An access token enhancer that will be applied to a new token before it is saved in the token store.
*
* @param accessTokenEnhancer the access token enhancer to set
*/
public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
this.accessTokenEnhancer = accessTokenEnhancer;
}
/**
* The validity (in seconds) of the refresh token. If less than or equal to zero then the tokens will be
* non-expiring.
*
* @param refreshTokenValiditySeconds The validity (in seconds) of the refresh token.
*/
public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
}
/**
* The default validity (in seconds) of the access token. Zero or negative for non-expiring tokens. If a client
* details service is set the validity period will be read from the client, defaulting to this value if not defined
* by the client.
*
* @param accessTokenValiditySeconds The validity (in seconds) of the access token.
*/
public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
}
/**
* Whether to support the refresh token.
*
* @param supportRefreshToken Whether to support the refresh token.
*/
public void setSupportRefreshToken(boolean supportRefreshToken) {
this.supportRefreshToken = supportRefreshToken;
}
/**
* Whether to reuse refresh tokens (until expired).
*
* @param reuseRefreshToken Whether to reuse refresh tokens (until expired).
*/
public void setReuseRefreshToken(boolean reuseRefreshToken) {
this.reuseRefreshToken = reuseRefreshToken;
}
/**
* The persistence strategy for token storage.
*
* @param tokenStore the store for access and refresh tokens.
*/
public void setTokenStore(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
/**
* An authentication manager that will be used (if provided) to check the user authentication when a token is
* refreshed.
*
* @param authenticationManager the authenticationManager to set
*/
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* The client details service to use for looking up clients (if necessary). Optional if the access token expiry is
* set globally via {@link #setAccessTokenValiditySeconds(int)}.
*
* @param clientDetailsService the client details service
*/
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
}
}
令牌存储
TokenStore
TokenStore 是 OAuth2 令牌的持久性接口,定义了存储及获取令牌的相关方法。
public interface TokenStore {
/**
* Read the authentication stored under the specified token value.
*
* @param token The token value under which the authentication is stored.
* @return The authentication, or null if none.
*/
OAuth2Authentication readAuthentication(OAuth2AccessToken token);
/**
* Read the authentication stored under the specified token value.
*
* @param token The token value under which the authentication is stored.
* @return The authentication, or null if none.
*/
OAuth2Authentication readAuthentication(String token);
/**
* Store an access token.
*
* @param token The token to store.
* @param authentication The authentication associated with the token.
*/
void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
/**
* Read an access token from the store.
*
* @param tokenValue The token value.
* @return The access token to read.
*/
OAuth2AccessToken readAccessToken(String tokenValue);
/**
* Remove an access token from the store.
*
* @param token The token to remove from the store.
*/
void removeAccessToken(OAuth2AccessToken token);
/**
* Store the specified refresh token in the store.
*
* @param refreshToken The refresh token to store.
* @param authentication The authentication associated with the refresh token.
*/
void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication);
/**
* Read a refresh token from the store.
*
* @param tokenValue The value of the token to read.
* @return The token.
*/
OAuth2RefreshToken readRefreshToken(String tokenValue);
/**
* @param token a refresh token
* @return the authentication originally used to grant the refresh token
*/
OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token);
/**
* Remove a refresh token from the store.
*
* @param token The token to remove from the store.
*/
void removeRefreshToken(OAuth2RefreshToken token);
/**
* Remove an access token using a refresh token. This functionality is necessary so refresh tokens can't be used to
* create an unlimited number of access tokens.
*
* @param refreshToken The refresh token.
*/
void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken);
/**
* Retrieve an access token stored against the provided authentication key, if it exists.
*
* @param authentication the authentication key for the access token
*
* @return the access token or null if there was none
*/
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
/**
* @param clientId the client id to search
* @param userName the user name to search
* @return a collection of access tokens
*/
Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName);
/**
* @param clientId the client id to search
* @return a collection of access tokens
*/
Collection<OAuth2AccessToken> findTokensByClientId(String clientId);
}
TokenStore 实现类
TokenStore 有以下实现类,每一个对应一种存储方式,可以使用内存、数据库、JWT、Redis 来保存我们的令牌。
默认使用的是 InMemoryTokenStore 来存储,如果用数据库,那么每次 token 服务查询、存储,都需要 SQL 操作,可以使用 JwtTokenStore,将令牌保存在 JWT 中。
使用 JwtTokenStore 存储,会涉及到 JWT 转换,框架也提供了 JwtAccessTokenConverter 类来处理。
它可以把令牌相关的数据进行编码(因此对于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是撤销一个已经授权令牌将会非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)。 另外一个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。
JwtAccessTokenConverter 使用 enhance 方法对 token 进行增强。
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
String tokenId = result.getValue();
// 原先的令牌,存为 jti: token
if (!info.containsKey(TOKEN_ID)) {
info.put(TOKEN_ID, tokenId);
}
else {
tokenId = (String) info.get(TOKEN_ID);
}
// 设置附加信息
result.setAdditionalInformation(info);
// JwtHelper.encode
result.setValue(encode(result, authentication));
// 处理刷新令牌
OAuth2RefreshToken refreshToken = result.getRefreshToken();
if (refreshToken != null) {
DefaultOAuth2AccessToken encodedRefreshToken = new DefaultOAuth2AccessToken(accessToken);
encodedRefreshToken.setValue(refreshToken.getValue());
// Refresh tokens do not expire unless explicitly of the right type
encodedRefreshToken.setExpiration(null);
try {
Map<String, Object> claims = objectMapper
.parseMap(JwtHelper.decode(refreshToken.getValue()).getClaims());
if (claims.containsKey(TOKEN_ID)) {
encodedRefreshToken.setValue(claims.get(TOKEN_ID).toString());
}
}
catch (IllegalArgumentException e) {
}
Map<String, Object> refreshTokenInfo = new LinkedHashMap<String, Object>(
accessToken.getAdditionalInformation());
refreshTokenInfo.put(TOKEN_ID, encodedRefreshToken.getValue());
refreshTokenInfo.put(ACCESS_TOKEN_ID, tokenId);
encodedRefreshToken.setAdditionalInformation(refreshTokenInfo);
DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(
encode(encodedRefreshToken, authentication));
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration();
encodedRefreshToken.setExpiration(expiration);
token = new DefaultExpiringOAuth2RefreshToken(encode(encodedRefreshToken, authentication), expiration);
}
result.setRefreshToken(token);
}
return result;
}
设置 JWT 令牌
通过以上源码分析,我们如果需要使用 JWT 令牌,那么我们需要设置 JWT 存储及转换器,并设置到 AuthorizationServerEndpointsConfigurer 配置中即可。
- 注入 TokenStore 及 JwtAccessTokenConverter
@Configuration
public class MyAuthorizationServerConfigurationBean {
// 令牌存储
@Bean
public TokenStore jwtTokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
// Jwt转换器
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123456");
return converter;
}
}
- AuthorizationServerEndpointsConfigurer 添加 TokenStore 及 JwtAccessTokenConverter
@Autowired
private TokenStore jwtTokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
// 端点配置
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置端点允许的请求方式
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
// 配置认证管理器
endpoints.authenticationManager(authenticationManager);
// JWT令牌转换器
endpoints.accessTokenConverter(jwtAccessTokenConverter);
// JWT 存储令牌
endpoints.tokenStore(jwtTokenStore);
}
使用密码模式访问令牌,返回了 JWT 令牌。
使用 JwtHelper.decode 方法进行解析,发现可以直接解析出我们的用户及其权限信息,实际这是不安全的。。。而且如果权限很多,那么这个 JWT 将会很长很长。。。
{"exp":1626466719,"user_name":"user","authorities":["ROLE_USER"],"jti":"1e303c89-a6e0-4a44-95e8-dc462060c1b6","client_id":"client","scope":["app"]}