初始化项目
This commit is contained in:
71
xtools-app-sys/xtools-app-sys-auth/pom.xml
Normal file
71
xtools-app-sys/xtools-app-sys-auth/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-app-sys</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>xtools-app-sys-auth</artifactId>
|
||||
|
||||
<!-- 依赖 -->
|
||||
<dependencies>
|
||||
<!-- xtools begin -->
|
||||
<!-- xtools-web 模块 -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-web</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-base -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-core</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-cache-redis -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-cache-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-mask -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-mask</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-web-filter -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-web-filter</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools end -->
|
||||
|
||||
<!-- 项目模块 begin -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-app-common-cache</artifactId>
|
||||
</dependency>
|
||||
<!-- 项目模块 end -->
|
||||
|
||||
<!-- spring begin -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<!-- spring end -->
|
||||
|
||||
<!-- fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet-api -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,235 @@
|
||||
package xtools.app.sys.auth.filter;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
import xtools.app.common.cache.enums.AppCache;
|
||||
import xtools.app.sys.auth.model.dto.LoginUserDto;
|
||||
import xtools.app.sys.auth.utils.AuthUtils;
|
||||
import xtools.app.sys.auth.utils.PremUtils;
|
||||
import xtools.app.sys.auth.whitelist.AuthWhitelist;
|
||||
import xtools.base.config.BaseParams;
|
||||
import xtools.boot.api.constant.BootCommonConstant;
|
||||
import xtools.boot.api.enums.ResultType;
|
||||
import xtools.boot.api.model.dto.Result;
|
||||
import xtools.boot.cache.redis.base.RedisService;
|
||||
import xtools.boot.core.holder.CommonHolder;
|
||||
import xtools.boot.core.utils.PathPatternUtils;
|
||||
import xtools.boot.core.utils.SpringContextUtils;
|
||||
import xtools.boot.mask.utils.MaskIgnoreUtils;
|
||||
import xtools.boot.web.filter.base.BaseFilter;
|
||||
import xtools.core.CollectionUtils;
|
||||
import xtools.core.StringUtils;
|
||||
import xtools.web.HeaderUtils;
|
||||
import xtools.web.HttpServletUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>Title : AuthFilter</p>
|
||||
* <p>Description : AuthFilter</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/1/31 21:18
|
||||
*/
|
||||
@Component
|
||||
public class AuthFilter extends BaseFilter implements Ordered, BaseParams {
|
||||
|
||||
/**
|
||||
* 微服务标识
|
||||
*/
|
||||
private static final String CLOUD_FLAG = String.valueOf(Boolean.TRUE);
|
||||
|
||||
/**
|
||||
* 缓存参数
|
||||
*/
|
||||
private static final AppCache CACHE_PARAM = AppCache.AUTH_CLOUD_TOKEN;
|
||||
|
||||
/**
|
||||
* 权限白名单
|
||||
*/
|
||||
private static final Set<String> AUTH_WHITE_LIST = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 登录白名单
|
||||
*/
|
||||
private static final Set<String> LOGIN_WHITE_LIST = new HashSet<>();
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return CP_NUM200 + CP_NUM1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilterBean() throws ServletException {
|
||||
super.initFilterBean();
|
||||
Collection<AuthWhitelist> beanList = SpringContextUtils.getBeanList(AuthWhitelist.class);
|
||||
if (CollectionUtils.isEmpty(beanList)) {
|
||||
return;
|
||||
}
|
||||
beanList.forEach(item -> {
|
||||
AUTH_WHITE_LIST.addAll(item.addAuth());
|
||||
LOGIN_WHITE_LIST.addAll(item.addLogin());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
// 判断是否为微服务请求
|
||||
if (Objects.equals(HeaderUtils.getHeader(request, BootCommonConstant.CLOUD), CLOUD_FLAG)) {
|
||||
// 微服务请求
|
||||
cloudAuth(request, response, filterChain);
|
||||
} else {
|
||||
// 常规请求
|
||||
auth(request, response, filterChain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微服务验证权限
|
||||
*
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
* @param filterChain 过滤器链
|
||||
*/
|
||||
private void cloudAuth(
|
||||
@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
String token = HeaderUtils.getHeader(request, BootCommonConstant.CLOUD_TOKEN);
|
||||
if (StringUtils.isBlank(token)) {
|
||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.UNAUTHORIZED, null)));
|
||||
return;
|
||||
}
|
||||
Long total = redisService.hashDelete(CACHE_PARAM.key(), token);
|
||||
if (Objects.isNull(total) || total < CP_NUM1) {
|
||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.UNAUTHORIZED, null)));
|
||||
return;
|
||||
}
|
||||
// 传递头部信息
|
||||
String uid = AuthUtils.getUid(request);
|
||||
String accessToken = AuthUtils.getAccessToken(request);
|
||||
if (StringUtils.isNotBlank(uid)) {
|
||||
CommonHolder.set(BootCommonConstant.UID, uid);
|
||||
}
|
||||
if (StringUtils.isNotBlank(accessToken)) {
|
||||
CommonHolder.set(BootCommonConstant.AUTHORIZATION, accessToken);
|
||||
// 校验忽略掩码
|
||||
checkIgnoreMask();
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 常规验证权限
|
||||
*
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
* @param filterChain 过滤器链
|
||||
*/
|
||||
private void auth(
|
||||
@NonNull HttpServletRequest request,
|
||||
@NonNull HttpServletResponse response,
|
||||
@NonNull FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
// 获取访问 uri
|
||||
String uri = HeaderUtils.getUri(request);
|
||||
|
||||
// 忽略权限校验
|
||||
if (checkAuthWhiteList(uri)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证uid
|
||||
String uid = AuthUtils.getUid(request);
|
||||
if (StringUtils.isBlank(uid)) {
|
||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.METHOD_NOT_ALLOWED, null)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验登录白名单
|
||||
if (checkLoginWhiteList(uri)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验登录权限
|
||||
String accessToken = AuthUtils.getAccessToken(request);
|
||||
LoginUserDto loginUser = AuthUtils.get(accessToken);
|
||||
if (Objects.isNull(loginUser)) {
|
||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.UNAUTHORIZED, null)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 校验 uri 访问权限
|
||||
String method = request.getMethod();
|
||||
if (!PremUtils.checkInterfacePerm(uri, method, loginUser.getRoleIds())) {
|
||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.FORBIDDEN, null)));
|
||||
return;
|
||||
}
|
||||
|
||||
CommonHolder.set(BootCommonConstant.UID, uid);
|
||||
CommonHolder.set(BootCommonConstant.AUTHORIZATION, accessToken);
|
||||
CommonHolder.set(BootCommonConstant.AUTH, loginUser);
|
||||
|
||||
// 校验忽略掩码
|
||||
checkIgnoreMask();
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验忽略掩码
|
||||
*/
|
||||
private void checkIgnoreMask() {
|
||||
if (AuthUtils.isIgnoreMask()) {
|
||||
// 忽略脱敏
|
||||
MaskIgnoreUtils.ignore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测权限白名单
|
||||
*
|
||||
* @param uri 访问 uri
|
||||
* @return 是否是白名单
|
||||
*/
|
||||
private boolean checkAuthWhiteList(String uri) {
|
||||
return PathPatternUtils.match(AUTH_WHITE_LIST, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测登录白名单
|
||||
*
|
||||
* @param uri 请求 uri
|
||||
* @return 是否是白名单
|
||||
*/
|
||||
private boolean checkLoginWhiteList(String uri) {
|
||||
return PathPatternUtils.match(LOGIN_WHITE_LIST, uri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//package xtools.app.sys.auth.holder;
|
||||
//
|
||||
//import xtools.app.sys.auth.model.dto.LoginUserDto;
|
||||
//import xtools.boot.api.exection.UnauthorizedError;
|
||||
//
|
||||
/// **
|
||||
// * <p>Title : LoginUserHolder</p>
|
||||
// * <p>Description : LoginUserHolder</p>
|
||||
// * <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
// * <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
// * <p>Company : org.xujun</p>
|
||||
// *
|
||||
// * @author : XuJun
|
||||
// * @version : 1.0.0
|
||||
// * @date : 2026/1/31 21:20
|
||||
// */
|
||||
//public class AuthHolder {
|
||||
//
|
||||
// /**
|
||||
// * 登录用户信息
|
||||
// */
|
||||
// private static final ScopedValue<LoginUserDto> LOGIN_USER_INFO = ScopedValue.newInstance();
|
||||
//
|
||||
// /**
|
||||
// * 获取登录用户信息 Holder
|
||||
// *
|
||||
// * @return 登录用户信息
|
||||
// */
|
||||
// public static ScopedValue<LoginUserDto> getScoped() {
|
||||
// return LOGIN_USER_INFO;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取登录用户信息
|
||||
// *
|
||||
// * @return 登录用户信息
|
||||
// */
|
||||
// public static LoginUserDto get() {
|
||||
// if (!LOGIN_USER_INFO.isBound()) {
|
||||
// throw new UnauthorizedError();
|
||||
// }
|
||||
// return LOGIN_USER_INFO.get();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,36 @@
|
||||
package xtools.app.sys.auth.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Title : InterfacePermDto</p>
|
||||
* <p>Description : InterfacePermDto</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/2 15:58
|
||||
*/
|
||||
@Data
|
||||
public class InterfacePermDto implements Serializable {
|
||||
|
||||
/**
|
||||
* 接口地址
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 接口类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 角色 ID 集合
|
||||
*/
|
||||
private List<Long> roleIds;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package xtools.app.sys.auth.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Title : LoginUserDto</p>
|
||||
* <p>Description : LoginUserDto</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/1/31 20:40
|
||||
*/
|
||||
@Data
|
||||
public class LoginUserDto implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户 ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
private String account;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 部门 ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 角色 ID 集合
|
||||
*/
|
||||
private List<Long> roleIds;
|
||||
|
||||
/**
|
||||
* 忽略脱敏时间
|
||||
*/
|
||||
private Long ignoreMaskTime;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package xtools.app.sys.auth.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>Title : TokenDto</p>
|
||||
* <p>Description : TokenDto</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/1/31 14:43
|
||||
*/
|
||||
@Data
|
||||
public class TokenDto implements Serializable {
|
||||
|
||||
/**
|
||||
* 数据 Token
|
||||
*/
|
||||
@Schema(description = "数据 Token", example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbi")
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 刷新 Token
|
||||
*/
|
||||
@Schema(description = "刷新 Token", example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbi")
|
||||
private String refreshToken;
|
||||
|
||||
/**
|
||||
* 数据 Token过期时间
|
||||
*/
|
||||
@Schema(description = "数据 Token过期时间", example = "1675000000000")
|
||||
private Long expiryTime;
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package xtools.app.sys.auth.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import xtools.app.common.cache.enums.AppCache;
|
||||
import xtools.app.sys.auth.model.dto.LoginUserDto;
|
||||
import xtools.app.sys.auth.model.dto.TokenDto;
|
||||
import xtools.base.config.BaseParams;
|
||||
import xtools.boot.api.constant.BootCommonConstant;
|
||||
import xtools.boot.api.exection.BizError;
|
||||
import xtools.boot.api.exection.UnauthorizedError;
|
||||
import xtools.boot.cache.redis.base.RedisService;
|
||||
import xtools.boot.cache.redis.utils.RedisUtils;
|
||||
import xtools.boot.core.holder.CommonHolder;
|
||||
import xtools.core.CollectionUtils;
|
||||
import xtools.core.StringUtils;
|
||||
import xtools.core.UuidUtils;
|
||||
import xtools.core.extend.CheckUtils;
|
||||
import xtools.web.HeaderUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* <p>Title : AuthUtils</p>
|
||||
* <p>Description : AuthUtils</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/1/31 20:44
|
||||
*/
|
||||
public class AuthUtils implements BaseParams {
|
||||
|
||||
/**
|
||||
* 缓存参数
|
||||
*/
|
||||
private static final AppCache CACHE_PARAM = AppCache.AUTH_SYS_USER;
|
||||
|
||||
/**
|
||||
* accessTokenKey
|
||||
*/
|
||||
private static final String ACCESS_TOKEN_KEY = "Authorization";
|
||||
|
||||
/**
|
||||
* accessToken
|
||||
*/
|
||||
private static final String ACCESS_TOKEN = "accessToken";
|
||||
|
||||
/**
|
||||
* refreshToken
|
||||
*/
|
||||
private static final String REFRESH_TOKEN = "refreshToken";
|
||||
|
||||
/**
|
||||
* user
|
||||
*/
|
||||
private static final String USER = "id";
|
||||
|
||||
/**
|
||||
* mask
|
||||
*/
|
||||
private static final String MASK = "mask";
|
||||
|
||||
/**
|
||||
* 缓存 KeyMap
|
||||
*/
|
||||
private static final Map<String, String> CACHE_KEY;
|
||||
|
||||
// 初始化
|
||||
static {
|
||||
String key = CACHE_PARAM.key();
|
||||
CACHE_KEY = new ConcurrentHashMap<>(CP_NUM16);
|
||||
CACHE_KEY.put(ACCESS_TOKEN, key + ACCESS_TOKEN + CP_COLON);
|
||||
CACHE_KEY.put(REFRESH_TOKEN, key + REFRESH_TOKEN + CP_COLON);
|
||||
CACHE_KEY.put(USER, key + USER + CP_COLON);
|
||||
CACHE_KEY.put(MASK, key + MASK + CP_COLON);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AccessTokenKey
|
||||
*
|
||||
* @return key
|
||||
*/
|
||||
public static String getAccessTokenKey() {
|
||||
return ACCESS_TOKEN_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AccessToken
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return AccessToken
|
||||
*/
|
||||
public static String getAccessToken(HttpServletRequest request) {
|
||||
return HeaderUtils.getRequestParam(request, getAccessTokenKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Uid
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return Uid
|
||||
*/
|
||||
public static String getUid(HttpServletRequest request) {
|
||||
return HeaderUtils.getRequestParam(request, BootCommonConstant.UID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Token
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
* @return Token 信息
|
||||
*/
|
||||
public static TokenDto createToken(LoginUserDto loginUser) {
|
||||
Long userId = loginUser.getId();
|
||||
remove(userId);
|
||||
|
||||
String accessToken = UuidUtils.get();
|
||||
String refreshToken = UuidUtils.get();
|
||||
|
||||
Long accessTokenExpiryTime = CACHE_PARAM.expireTime();
|
||||
Long refreshTokenExpiryTime = accessTokenExpiryTime + CACHE_PARAM.expireTime();
|
||||
Long userExpiryTime = System.currentTimeMillis() + accessTokenExpiryTime * 1000;
|
||||
|
||||
RedisService redis = RedisUtils.get();
|
||||
// 保存 accessToken信息
|
||||
redis.set(getCacheKey(ACCESS_TOKEN, accessToken),
|
||||
JSONObject.of("expiryTime", userExpiryTime, "loginUser", loginUser), refreshTokenExpiryTime);
|
||||
// 保存 refreshToken信息
|
||||
redis.set(getCacheKey(REFRESH_TOKEN, refreshToken), accessToken, refreshTokenExpiryTime);
|
||||
// 保存用户 Token 信息
|
||||
redis.set(getCacheKey(USER, userId),
|
||||
JSONObject.of(ACCESS_TOKEN, accessToken, REFRESH_TOKEN, refreshToken), refreshTokenExpiryTime);
|
||||
|
||||
// 构建 Token 信息
|
||||
TokenDto token = new TokenDto();
|
||||
token.setAccessToken(accessToken);
|
||||
token.setRefreshToken(refreshToken);
|
||||
token.setExpiryTime(userExpiryTime);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 LoginUser
|
||||
*
|
||||
* @return LoginUser
|
||||
*/
|
||||
public static LoginUserDto get() {
|
||||
Map<String, Object> map = CommonHolder.get();
|
||||
if (CollectionUtils.isEmpty(map)) {
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
if (map.get(BootCommonConstant.AUTH) instanceof LoginUserDto loginUserDto) {
|
||||
return loginUserDto;
|
||||
}
|
||||
if (map.get(BootCommonConstant.AUTHORIZATION) instanceof String accessToken) {
|
||||
return get(accessToken);
|
||||
}
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 LoginUser
|
||||
*
|
||||
* @param id 用户 ID
|
||||
* @return LoginUser
|
||||
*/
|
||||
public static LoginUserDto get(Long id) {
|
||||
RedisService redis = RedisUtils.get();
|
||||
JSONObject userCache = redis.get(getCacheKey(USER, id), JSONObject.class);
|
||||
if (CollectionUtils.isEmpty(userCache)) {
|
||||
return null;
|
||||
}
|
||||
String accessToken = userCache.getString(ACCESS_TOKEN);
|
||||
return get(accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存获取 LoginUser
|
||||
*
|
||||
* @param accessToken AccessToken
|
||||
* @return LoginUser
|
||||
*/
|
||||
public static LoginUserDto get(String accessToken) {
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
return null;
|
||||
}
|
||||
return getFromCache(accessToken, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存中登录用户信息
|
||||
*
|
||||
* @param idList 用户 ID 集合
|
||||
*/
|
||||
public static void remove(List<Long> idList) {
|
||||
if (CollectionUtils.isEmpty(idList)) {
|
||||
return;
|
||||
}
|
||||
for (Long id : idList) {
|
||||
remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存中登录用户信息
|
||||
*
|
||||
* @param id 用户 ID
|
||||
*/
|
||||
public static void remove(Long id) {
|
||||
RedisService redis = RedisUtils.get();
|
||||
JSONObject userCache = redis.get(getCacheKey(USER, id), JSONObject.class);
|
||||
if (CollectionUtils.isEmpty(userCache)) {
|
||||
return;
|
||||
}
|
||||
String accessToken = userCache.getString(ACCESS_TOKEN);
|
||||
String refreshToken = userCache.getString(REFRESH_TOKEN);
|
||||
redis.del(getCacheKey(ACCESS_TOKEN, accessToken));
|
||||
redis.del(getCacheKey(REFRESH_TOKEN, refreshToken));
|
||||
redis.del(getCacheKey(USER, id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新 Token
|
||||
*
|
||||
* @param refreshToken RefreshToken
|
||||
* @return Token
|
||||
*/
|
||||
public static TokenDto refreshToken(String refreshToken) {
|
||||
if (StringUtils.isBlank(refreshToken)) {
|
||||
throw new BizError("refreshToken 不能为空");
|
||||
}
|
||||
RedisService redis = RedisUtils.get();
|
||||
String accessToken = redis.get(getCacheKey(REFRESH_TOKEN, refreshToken), String.class);
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
throw new BizError("token已过期,请重新登录");
|
||||
}
|
||||
LoginUserDto loginUser = getFromCache(accessToken, false);
|
||||
if (loginUser == null) {
|
||||
throw new BizError("token已过期,请重新登录");
|
||||
}
|
||||
return createToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否忽略脱敏
|
||||
*
|
||||
* @return 是否忽略脱敏
|
||||
*/
|
||||
public static boolean isIgnoreMask() {
|
||||
LoginUserDto loginUser = get();
|
||||
if (Objects.isNull(loginUser)) {
|
||||
throw new BizError("请先登录");
|
||||
}
|
||||
RedisService redis = RedisUtils.get();
|
||||
Long time = redis.get(getCacheKey(MASK, loginUser.getId()), Long.class);
|
||||
return CheckUtils.id(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置忽略脱敏
|
||||
*/
|
||||
public static void setIgnoreMask() {
|
||||
LoginUserDto loginUser = get();
|
||||
if (Objects.isNull(loginUser)) {
|
||||
throw new BizError("请先登录");
|
||||
}
|
||||
RedisService redis = RedisUtils.get();
|
||||
redis.set(getCacheKey(MASK, loginUser.getId()), Instant.now().toEpochMilli(), CACHE_PARAM.expireTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除忽略脱敏
|
||||
*/
|
||||
public static void removeIgnoreMask() {
|
||||
LoginUserDto loginUser = get();
|
||||
if (Objects.isNull(loginUser)) {
|
||||
throw new BizError("请先登录");
|
||||
}
|
||||
RedisService redis = RedisUtils.get();
|
||||
redis.del(getCacheKey(MASK, loginUser.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存获取 LoginUser
|
||||
*
|
||||
* @param accessToken AccessToken
|
||||
* @param checkTime 是否检验有效期
|
||||
* @return LoginUser
|
||||
*/
|
||||
private static LoginUserDto getFromCache(String accessToken, boolean checkTime) {
|
||||
JSONObject data = RedisUtils.get().get(getCacheKey(ACCESS_TOKEN, accessToken), JSONObject.class);
|
||||
if (CollectionUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
if (checkTime) {
|
||||
Long expiryTime = data.getLong("expiryTime");
|
||||
if (expiryTime < System.currentTimeMillis()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return JSON.to(LoginUserDto.class, data.getJSONObject("loginUser"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存 key
|
||||
*
|
||||
* @param keyType key 类型
|
||||
* @param key Key
|
||||
* @return 缓存 key
|
||||
*/
|
||||
private static String getCacheKey(String keyType, Object key) {
|
||||
String keyPrefix = CACHE_KEY.get(keyType);
|
||||
if (StringUtils.isBlank(keyPrefix)) {
|
||||
throw new BizError("keyType 错误");
|
||||
}
|
||||
return keyPrefix + key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package xtools.app.sys.auth.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import xtools.app.common.cache.enums.AppCache;
|
||||
import xtools.app.sys.auth.model.dto.InterfacePermDto;
|
||||
import xtools.base.config.BaseParams;
|
||||
import xtools.boot.cache.redis.base.RedisService;
|
||||
import xtools.boot.cache.redis.utils.RedisUtils;
|
||||
import xtools.core.CollectionUtils;
|
||||
import xtools.core.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Title : PremUtils</p>
|
||||
* <p>Description : PremUtils</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/2/2 18:31
|
||||
*/
|
||||
public class PremUtils implements BaseParams {
|
||||
|
||||
/**
|
||||
* 缓存参数
|
||||
*/
|
||||
private static final AppCache CACHE_PARAM = AppCache.AUTH_SYS_URI;
|
||||
|
||||
/**
|
||||
* 接口权限类型映射
|
||||
*/
|
||||
private static final Map<String, String> PERM_TYPE_MAP;
|
||||
|
||||
// 初始化
|
||||
static {
|
||||
PERM_TYPE_MAP = new HashMap<>(CP_NUM7);
|
||||
PERM_TYPE_MAP.put("1", "GET");
|
||||
PERM_TYPE_MAP.put("2", "POST");
|
||||
PERM_TYPE_MAP.put("3", "PUT");
|
||||
PERM_TYPE_MAP.put("4", "DELETE");
|
||||
PERM_TYPE_MAP.put("5", "PATCH");
|
||||
PERM_TYPE_MAP.put("6", "HEAD");
|
||||
PERM_TYPE_MAP.put("7", "OPTIONS");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口权限类型
|
||||
*
|
||||
* @param type 接口权限类型
|
||||
* @return 接口权限类型
|
||||
*/
|
||||
public static String getPermType(String type) {
|
||||
return PERM_TYPE_MAP.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口权限缓存 key
|
||||
*
|
||||
* @return 缓存 key
|
||||
*/
|
||||
public static String getCacheKey() {
|
||||
return CACHE_PARAM.key();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块名称
|
||||
*
|
||||
* @param uri 请求 uri
|
||||
* @return 模块名称
|
||||
*/
|
||||
public static String getModuleName(String uri) {
|
||||
String[] split = uri.split(CP_SLASH);
|
||||
int length = split.length;
|
||||
if (length <= CP_NUM1) {
|
||||
return CP_EMPTY;
|
||||
} else if (length == CP_NUM2) {
|
||||
return split[CP_NUM1];
|
||||
} else {
|
||||
return split[CP_NUM1] + CP_LINE + split[CP_NUM2];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查接口权限
|
||||
*
|
||||
* @param uri 请求 uri
|
||||
* @param type 接口权限类型
|
||||
* @param roleIds 角色 ID 集合
|
||||
* @return 是否有权限
|
||||
*/
|
||||
public static boolean checkInterfacePerm(String uri, String type, List<Long> roleIds) {
|
||||
String moduleName = getModuleName(uri);
|
||||
if (StringUtils.isBlank(moduleName)) {
|
||||
return false;
|
||||
}
|
||||
String cacheKey = moduleName + CP_COLON + type;
|
||||
RedisService redis = RedisUtils.get();
|
||||
List<?> list = redis.hashGet(getCacheKey(), cacheKey, List.class);
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return false;
|
||||
}
|
||||
PathMatcher pm = new AntPathMatcher();
|
||||
for (Object obj : list) {
|
||||
InterfacePermDto item = JSON.to(InterfacePermDto.class, obj);
|
||||
String permUri = item.getUri();
|
||||
if (!pm.match(permUri, uri)) {
|
||||
continue;
|
||||
}
|
||||
List<Long> permRoleIds = item.getRoleIds();
|
||||
if (CollectionUtils.isEmpty(permRoleIds)) {
|
||||
return false;
|
||||
}
|
||||
return roleIds.stream().anyMatch(permRoleIds::contains);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package xtools.app.sys.auth.whitelist;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>Title : AuthWhitelist</p>
|
||||
* <p>Description : AuthWhitelist</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 1.0.0
|
||||
* @date : 2026/4/2 11:38
|
||||
*/
|
||||
public interface AuthWhitelist {
|
||||
|
||||
/**
|
||||
* 添加权限白名单
|
||||
*
|
||||
* @return 权限白名单
|
||||
*/
|
||||
default Set<String> addAuth() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加登录白名单
|
||||
*
|
||||
* @return 登录白名单
|
||||
*/
|
||||
default Set<String> addLogin() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user