|
@@ -1,40 +1,36 @@
|
|
|
package com.chelvc.framework.oauth.context;
|
|
|
|
|
|
-import java.io.Serializable;
|
|
|
-import java.time.Duration;
|
|
|
import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+import javax.servlet.ServletRequestEvent;
|
|
|
+import javax.servlet.ServletRequestListener;
|
|
|
|
|
|
-import com.chelvc.framework.base.context.SessionContextHolder;
|
|
|
import com.chelvc.framework.common.model.Terminal;
|
|
|
-import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
import com.chelvc.framework.common.util.StringUtils;
|
|
|
-import com.chelvc.framework.redis.util.RedisUtils;
|
|
|
-import com.google.common.collect.Maps;
|
|
|
import lombok.NonNull;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.data.redis.core.RedisTemplate;
|
|
|
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|
|
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
|
|
import org.springframework.security.oauth2.jwt.Jwt;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
|
|
|
/**
|
|
|
- * Oauth上下文工具类
|
|
|
+ * OAuth上下文工具类
|
|
|
*
|
|
|
* @author Woody
|
|
|
* @date 2023/4/5
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-public class OauthContextHolder {
|
|
|
+@Component
|
|
|
+public class OauthContextHolder implements ServletRequestListener {
|
|
|
/**
|
|
|
- * 主体身份标识
|
|
|
+ * JWT身份标识
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_ID = "id";
|
|
|
+ public static final String JWT_CLAIM_JTI = "jti";
|
|
|
|
|
|
/**
|
|
|
- * JWT身份标识
|
|
|
+ * 应用范围标识
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_JTI = "jti";
|
|
|
+ public static final String JWT_CLAIM_SCOPE = "scope";
|
|
|
|
|
|
/**
|
|
|
* JWT手机号标识
|
|
@@ -42,46 +38,51 @@ public class OauthContextHolder {
|
|
|
public static final String JWT_CLAIM_MOBILE = "mobile";
|
|
|
|
|
|
/**
|
|
|
- * 业务类型标识
|
|
|
+ * 用户账号标识
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_BUSINESS = "business";
|
|
|
+ public static final String JWT_CLAIM_USER_NAME = "user_name";
|
|
|
|
|
|
/**
|
|
|
- * 用户账号标识
|
|
|
+ * 用户权限标识
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_USER_NAME = "user_name";
|
|
|
+ public static final String JWT_CLAIM_AUTHORITIES = "authorities";
|
|
|
|
|
|
/**
|
|
|
- * 客户端账号标识
|
|
|
+ * 刷新令牌上下文
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_CLIENT_ID = "client_id";
|
|
|
+ private static final ThreadLocal<OAuth2RefreshToken> REFRESH_TOKEN_CONTEXT = new ThreadLocal<>();
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void requestDestroyed(ServletRequestEvent event) {
|
|
|
+ clearOauthContext();
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * 用户权限标识
|
|
|
+ * 获取当前刷新令牌
|
|
|
+ *
|
|
|
+ * @return 刷新令牌实例
|
|
|
*/
|
|
|
- public static final String JWT_CLAIM_AUTHORITIES = "authorities";
|
|
|
+ public static OAuth2RefreshToken getRefreshToken() {
|
|
|
+ return REFRESH_TOKEN_CONTEXT.get();
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * 获取主体身份
|
|
|
+ * 设置当前刷新令牌
|
|
|
*
|
|
|
- * @param jwt JWT对象
|
|
|
- * @return 主体身份标识
|
|
|
+ * @param refreshToken 刷新令牌实例
|
|
|
*/
|
|
|
- public static Long getId(Jwt jwt) {
|
|
|
- return ObjectUtils.ifNull(jwt, t -> ObjectUtils.object2long(t.getClaim(JWT_CLAIM_ID)));
|
|
|
+ public static void setRefreshToken(OAuth2RefreshToken refreshToken) {
|
|
|
+ REFRESH_TOKEN_CONTEXT.set(refreshToken);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取主体身份
|
|
|
*
|
|
|
- * @param token OAuth2访问令牌对象
|
|
|
+ * @param jwt JWT对象
|
|
|
* @return 主体身份标识
|
|
|
*/
|
|
|
- public static Long getId(OAuth2AccessToken token) {
|
|
|
- return ObjectUtils.ifNull(
|
|
|
- ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
|
|
|
- info -> ObjectUtils.object2long(info.get(JWT_CLAIM_ID))
|
|
|
- );
|
|
|
+ public static Long getId(Jwt jwt) {
|
|
|
+ return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_USER_NAME), Long::parseLong);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -95,13 +96,13 @@ public class OauthContextHolder {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取用户标识
|
|
|
+ * 获取应用范围
|
|
|
*
|
|
|
* @param jwt JWT对象
|
|
|
- * @return 用户标识
|
|
|
+ * @return 应用范围
|
|
|
*/
|
|
|
- public static String getUser(Jwt jwt) {
|
|
|
- return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_USER_NAME), (String) null);
|
|
|
+ public static String getScope(Jwt jwt) {
|
|
|
+ return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_SCOPE), (String) null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -114,63 +115,6 @@ public class OauthContextHolder {
|
|
|
return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_MOBILE), (String) null);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取手机号
|
|
|
- *
|
|
|
- * @param token OAuth2访问令牌对象
|
|
|
- * @return 手机号
|
|
|
- */
|
|
|
- public static String getMobile(OAuth2AccessToken token) {
|
|
|
- return ObjectUtils.ifNull(
|
|
|
- ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
|
|
|
- info -> StringUtils.ifEmpty((String) info.get(JWT_CLAIM_MOBILE), (String) null)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取业务类型
|
|
|
- *
|
|
|
- * @param jwt JWT对象
|
|
|
- * @return 业务类型
|
|
|
- */
|
|
|
- public static String getBusiness(Jwt jwt) {
|
|
|
- return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_BUSINESS), (String) null);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取业务类型
|
|
|
- *
|
|
|
- * @param id 会话ID
|
|
|
- * @param redisTemplate Redis操作模版实例
|
|
|
- * @return 业务类型
|
|
|
- */
|
|
|
- public static String getBusiness(@NonNull Serializable id, @NonNull RedisTemplate<String, Object> redisTemplate) {
|
|
|
- return (String) redisTemplate.opsForHash().get(buildAccessTokenKey(id), "business");
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取业务类型
|
|
|
- *
|
|
|
- * @param token OAuth2访问令牌对象
|
|
|
- * @return 业务类型
|
|
|
- */
|
|
|
- public static String getBusiness(OAuth2AccessToken token) {
|
|
|
- return ObjectUtils.ifNull(
|
|
|
- ObjectUtils.ifNull(token, OAuth2AccessToken::getAdditionalInformation),
|
|
|
- info -> StringUtils.ifEmpty((String) info.get(JWT_CLAIM_BUSINESS), (String) null)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取客户端标识
|
|
|
- *
|
|
|
- * @param jwt JWT对象
|
|
|
- * @return 客户端标识
|
|
|
- */
|
|
|
- public static String getClient(Jwt jwt) {
|
|
|
- return jwt == null ? null : StringUtils.ifEmpty(jwt.getClaimAsString(JWT_CLAIM_CLIENT_ID), (String) null);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取用户授权信息
|
|
|
*
|
|
@@ -189,71 +133,20 @@ public class OauthContextHolder {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 构建令牌标识
|
|
|
+ * 构建访问令牌标识
|
|
|
*
|
|
|
- * @param id 会话ID
|
|
|
+ * @param principal 主体标识
|
|
|
+ * @param terminal 终端信息
|
|
|
* @return 令牌标识
|
|
|
*/
|
|
|
- public static String buildAccessTokenKey(@NonNull Serializable id) {
|
|
|
- return buildAccessTokenKey(id, SessionContextHolder.getTerminal());
|
|
|
+ public static String buildAccessTokenKey(@NonNull Object principal, @NonNull Terminal terminal) {
|
|
|
+ return String.format("token:%s:%s", terminal, principal);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 构建令牌标识
|
|
|
- *
|
|
|
- * @param id 会话ID
|
|
|
- * @param terminal 终端信息
|
|
|
- * @return 令牌标识
|
|
|
- */
|
|
|
- public static String buildAccessTokenKey(@NonNull Serializable id, @NonNull Terminal terminal) {
|
|
|
- return String.format("token:%s:%s", id, terminal);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 存储访问令牌
|
|
|
- *
|
|
|
- * @param token token OAuth2访问令牌对象
|
|
|
- * @param redisTemplate Redis操作模版实例
|
|
|
- */
|
|
|
- public static void storeAccessToken(@NonNull OAuth2AccessToken token,
|
|
|
- @NonNull RedisTemplate<String, Object> redisTemplate) {
|
|
|
- String key = buildAccessTokenKey(getId(token));
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- log.debug("Saving redis token: {}", key);
|
|
|
- }
|
|
|
- Map<String, Object> context = Maps.newHashMapWithExpectedSize(2);
|
|
|
- context.put("value", token.getValue());
|
|
|
- String business = getBusiness(token);
|
|
|
- if (StringUtils.nonEmpty(business)) {
|
|
|
- context.put("business", business);
|
|
|
- }
|
|
|
- long millis = Math.max(token.getExpiration().getTime() - System.currentTimeMillis(), 0);
|
|
|
- RedisUtils.hmset(redisTemplate, key, context, Duration.ofMillis(millis));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 删除访问令牌
|
|
|
- *
|
|
|
- * @param token token OAuth2访问令牌对象
|
|
|
- * @param redisTemplate Redis操作模版实例
|
|
|
- */
|
|
|
- public static void removeAccessToken(@NonNull OAuth2AccessToken token,
|
|
|
- @NonNull RedisTemplate<String, Object> redisTemplate) {
|
|
|
- String key = buildAccessTokenKey(getId(token));
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- log.debug("Saving redis token: {}", key);
|
|
|
- }
|
|
|
- redisTemplate.delete(key);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取访问令牌
|
|
|
- *
|
|
|
- * @param jwt JWT实例
|
|
|
- * @param redisTemplate Redis操作模版实例
|
|
|
- * @return 访问令牌
|
|
|
+ * 清空OAuth上下文
|
|
|
*/
|
|
|
- public static String getAccessToken(@NonNull Jwt jwt, @NonNull RedisTemplate<String, Object> redisTemplate) {
|
|
|
- return (String) redisTemplate.opsForHash().get(buildAccessTokenKey(getId(jwt)), "value");
|
|
|
+ public static void clearOauthContext() {
|
|
|
+ REFRESH_TOKEN_CONTEXT.remove();
|
|
|
}
|
|
|
}
|