init: 导入RuoYi‑Vue‑Plus 6.X完整代码
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-satoken</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-satoken 权限认证
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RuoYi Common Redis-->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- api模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- spring-webmvc 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot4-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 jwt -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 本地缓存 -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.dromara.common.satoken.config;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import org.dromara.common.core.factory.YmlPropertySourceFactory;
|
||||
import org.dromara.common.satoken.core.dao.PlusSaTokenDao;
|
||||
import org.dromara.common.satoken.core.service.SaPermissionImpl;
|
||||
import org.dromara.common.satoken.handler.SaTokenExceptionHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
/**
|
||||
* sa-token 配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@PropertySource(value = "classpath:common-satoken.yml", factory = YmlPropertySourceFactory.class)
|
||||
public class SaTokenConfig {
|
||||
|
||||
/**
|
||||
* 创建 Sa-Token JWT 登录逻辑。
|
||||
*
|
||||
* @return JWT 登录逻辑
|
||||
*/
|
||||
@Bean
|
||||
public StpLogic getStpLogicJwt() {
|
||||
// Sa-Token 整合 jwt (简单模式)
|
||||
return new StpLogicJwtForSimple();
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限接口实现(使用bean注入方便用户替换)
|
||||
*/
|
||||
@Bean
|
||||
public StpInterface stpInterface() {
|
||||
return new SaPermissionImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义dao层存储
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenDao saTokenDao() {
|
||||
return new PlusSaTokenDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenExceptionHandler saTokenExceptionHandler() {
|
||||
return new SaTokenExceptionHandler();
|
||||
}
|
||||
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
package org.dromara.common.satoken.core.dao;
|
||||
|
||||
import cn.dev33.satoken.dao.auto.SaTokenDaoBySessionFollowObject;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
|
||||
* <p>
|
||||
* 采用 caffeine + redis 多级缓存 优化并发查询效率
|
||||
* <p>
|
||||
* SaTokenDaoBySessionFollowObject 是 SaTokenDao 子集简化了session方法处理
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject {
|
||||
|
||||
private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
|
||||
// 设置最后一次写入或访问后经过固定时间过期
|
||||
.expireAfterWrite(5, TimeUnit.SECONDS)
|
||||
// 初始的缓存空间大小
|
||||
.initialCapacity(100)
|
||||
// 缓存的最大条数
|
||||
.maximumSize(1000)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 获取Value,如无返空
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return getCacheValue(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入Value,并设定存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
writeValue(key, value, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, value, true);
|
||||
invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Value
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
if (RedisUtils.deleteObject(key)) {
|
||||
invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Value的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return toTimeoutSeconds(RedisUtils.getTimeToLive(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改Value的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取Object,如无返空
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return getCacheValue(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Object (指定反序列化类型),如无返空
|
||||
*
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
@Override
|
||||
public <T> T getObject(String key, Class<T> classType) {
|
||||
return classType.cast(getCacheValue(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入Object,并设定存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
writeValue(key, object, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Object (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, object, true);
|
||||
invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Object
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
if (RedisUtils.deleteObject(key)) {
|
||||
invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Object的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return toTimeoutSeconds(RedisUtils.getTimeToLive(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改Object的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
String pattern = prefix + "*" + keyword + "*";
|
||||
String cacheKey = pattern + start + StringUtils.COLON + size + StringUtils.COLON + sortType;
|
||||
return (List<String>) CAFFEINE.get(cacheKey, k -> {
|
||||
Collection<String> keys = RedisUtils.keys(pattern);
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存读取对象。
|
||||
*
|
||||
* @param key 缓存键
|
||||
* @return 缓存值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getCacheValue(String key) {
|
||||
return (T) CAFFEINE.get(key, RedisUtils::getCacheObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存值并刷新本地缓存。
|
||||
*
|
||||
* @param key 缓存键
|
||||
* @param value 缓存值
|
||||
* @param timeout 超时时间
|
||||
*/
|
||||
private void writeValue(String key, Object value, long timeout) {
|
||||
if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
if (timeout == NEVER_EXPIRE) {
|
||||
RedisUtils.setCacheObject(key, value);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||
}
|
||||
invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除本地缓存。
|
||||
*
|
||||
* @param key 缓存键
|
||||
*/
|
||||
private void invalidate(String key) {
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Redis TTL 转为秒。
|
||||
*
|
||||
* @param timeoutRedis Redis TTL 毫秒值
|
||||
* @return Sa-Token 需要的秒值
|
||||
*/
|
||||
private long toTimeoutSeconds(long timeoutRedis) {
|
||||
// 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿
|
||||
return timeoutRedis < 0 ? timeoutRedis : timeoutRedis / 1000 + 1;
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.dromara.common.satoken.core.service;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.service.PermissionService;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.model.LoginUser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* sa-token 权限管理实现类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class SaPermissionImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 获取指定登录对象的菜单权限列表。
|
||||
*
|
||||
* @param loginId 登录ID
|
||||
* @param loginType 登录类型
|
||||
* @return 菜单权限列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
return resolvePermissionList(loginId, LoginUser::getMenuPermission, PermissionService::getMenuPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定登录对象的角色权限列表。
|
||||
*
|
||||
* @param loginId 登录ID
|
||||
* @param loginType 登录类型
|
||||
* @return 角色权限列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
return resolvePermissionList(loginId, LoginUser::getRolePermission, PermissionService::getRolePermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前登录对象的权限列表。
|
||||
*
|
||||
* @param loginId 登录ID
|
||||
* @param localPermissionExtractor 当前登录用户权限提取器
|
||||
* @param remotePermissionExtractor 远程权限提取器
|
||||
* @return 权限列表
|
||||
*/
|
||||
private List<String> resolvePermissionList(Object loginId,
|
||||
java.util.function.Function<LoginUser, Collection<String>> localPermissionExtractor,
|
||||
BiFunction<PermissionService, Long, Collection<String>> remotePermissionExtractor) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) {
|
||||
PermissionService permissionService = getPermissionService();
|
||||
if (ObjectUtil.isNotNull(permissionService)) {
|
||||
return new ArrayList<>(remotePermissionExtractor.apply(permissionService, resolveUserId(loginId)));
|
||||
}
|
||||
throw new ServiceException("PermissionService 实现类不存在");
|
||||
}
|
||||
Collection<String> permissionList = localPermissionExtractor.apply(loginUser);
|
||||
if (CollUtil.isNotEmpty(permissionList)) {
|
||||
return new ArrayList<>(permissionList);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限服务实现。
|
||||
*
|
||||
* @return 权限服务
|
||||
*/
|
||||
private PermissionService getPermissionService() {
|
||||
try {
|
||||
return SpringUtils.getBean(PermissionService.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从登录ID中提取用户ID。
|
||||
*
|
||||
* @param loginId 登录ID
|
||||
* @return 用户ID
|
||||
*/
|
||||
private Long resolveUserId(Object loginId) {
|
||||
String loginIdStr = loginId.toString();
|
||||
int separatorIndex = loginIdStr.indexOf(':');
|
||||
if (separatorIndex < 0 || separatorIndex == loginIdStr.length() - 1) {
|
||||
throw new ServiceException("登录ID格式错误");
|
||||
}
|
||||
return Long.parseLong(loginIdStr.substring(separatorIndex + 1));
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package org.dromara.common.satoken.handler;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* SaToken异常处理器
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class SaTokenExceptionHandler {
|
||||
|
||||
/**
|
||||
* 处理权限码校验失败异常。
|
||||
*
|
||||
* @param e 异常信息
|
||||
* @param request 当前请求
|
||||
* @return 统一失败响应
|
||||
*/
|
||||
@ExceptionHandler({NotPermissionException.class, NotRoleException.class})
|
||||
public R<Void> handleNotAccessException(RuntimeException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
String reason = e instanceof NotRoleException ? "角色权限校验失败" : "权限码校验失败";
|
||||
log.error("请求地址'{}',{}'{}'", requestURI, reason, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未登录或登录态失效异常。
|
||||
*
|
||||
* @param e 异常信息
|
||||
* @param request 当前请求
|
||||
* @return 统一失败响应
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
|
||||
String msg = switch (e.getType()) {
|
||||
case NotLoginException.TOKEN_TIMEOUT,
|
||||
NotLoginException.TOKEN_FREEZE -> "登录已过期,请重新登录";
|
||||
case NotLoginException.BE_REPLACED -> "当前账号已在其他设备登录,您已被强制下线";
|
||||
case NotLoginException.KICK_OUT -> "账号已被管理员强制下线";
|
||||
default -> "登录状态异常,请重新登录";
|
||||
};
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, msg);
|
||||
}
|
||||
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package org.dromara.common.satoken.utils;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.constant.SystemConstants;
|
||||
import org.dromara.common.core.enums.UserType;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.system.api.model.LoginUser;
|
||||
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
* <p>
|
||||
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||
* <p>
|
||||
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LoginHelper {
|
||||
|
||||
public static final String LOGIN_USER_KEY = "loginUser";
|
||||
public static final String USER_KEY = "userId";
|
||||
public static final String USER_NAME_KEY = "userName";
|
||||
public static final String DEPT_KEY = "deptId";
|
||||
public static final String DEPT_NAME_KEY = "deptName";
|
||||
public static final String DEPT_CATEGORY_KEY = "deptCategory";
|
||||
public static final String CLIENT_KEY = "clientid";
|
||||
public static final String CLIENT_ACCESS_PATH_KEY = "clientAccessPath";
|
||||
public static final String CLIENT_IP_WHITELIST_KEY = "clientIpWhitelist";
|
||||
|
||||
/**
|
||||
* 登录系统 基于 设备类型
|
||||
* 针对相同用户体系不同设备
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
* @param model 配置参数
|
||||
*/
|
||||
public static void login(LoginUser loginUser, SaLoginParameter model) {
|
||||
model = ObjectUtil.defaultIfNull(model, new SaLoginParameter());
|
||||
fillRequestContext(loginUser, model);
|
||||
StpUtil.login(loginUser.getLoginId(),
|
||||
model.setExtra(USER_KEY, loginUser.getUserId())
|
||||
.setExtra(USER_NAME_KEY, loginUser.getUsername())
|
||||
.setExtra(DEPT_KEY, loginUser.getDeptId())
|
||||
.setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
|
||||
.setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
|
||||
);
|
||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在登录时补充当前请求上下文,避免登录态中的终端信息缺失。
|
||||
*
|
||||
* @param loginUser 登录用户
|
||||
* @param model 登录参数
|
||||
*/
|
||||
private static void fillRequestContext(LoginUser loginUser, SaLoginParameter model) {
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
if (ObjectUtil.isNull(request)) {
|
||||
return;
|
||||
}
|
||||
String ip = ServletUtils.getClientIP(request);
|
||||
if (StringUtils.isBlank(loginUser.getIpaddr())) {
|
||||
loginUser.setIpaddr(ip);
|
||||
}
|
||||
if (StringUtils.isBlank(loginUser.getLoginLocation()) && StringUtils.isNotBlank(ip)) {
|
||||
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
}
|
||||
UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
|
||||
if (StringUtils.isBlank(loginUser.getBrowser())) {
|
||||
loginUser.setBrowser(userAgent.getBrowser().getName());
|
||||
}
|
||||
if (StringUtils.isBlank(loginUser.getOs())) {
|
||||
loginUser.setOs(userAgent.getOs().getName());
|
||||
}
|
||||
if (StringUtils.isBlank(loginUser.getDeviceType()) && StringUtils.isNotBlank(model.getDeviceType())) {
|
||||
loginUser.setDeviceType(model.getDeviceType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Token 对应的登录用户信息。
|
||||
*
|
||||
* @return 登录用户
|
||||
*/
|
||||
public static <T extends LoginUser> T getLoginUser() {
|
||||
try {
|
||||
return getLoginUser(StpUtil.getTokenSession());
|
||||
} catch (NotLoginException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定 Token 获取登录用户信息。
|
||||
*
|
||||
* @param token Token
|
||||
* @return 登录用户
|
||||
*/
|
||||
public static <T extends LoginUser> T getLoginUser(String token) {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
return null;
|
||||
}
|
||||
SaSession session;
|
||||
try {
|
||||
session = StpUtil.getTokenSessionByToken(token);
|
||||
} catch (NotLoginException e) {
|
||||
return null;
|
||||
}
|
||||
return getLoginUser(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从会话中读取登录用户。
|
||||
*
|
||||
* @param session 登录会话
|
||||
* @return 登录用户
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends LoginUser> T getLoginUser(SaSession session) {
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
return (T) session.get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户 ID。
|
||||
*
|
||||
* @return 用户 ID
|
||||
*/
|
||||
public static Long getUserId() {
|
||||
return Convert.toLong(getExtra(USER_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户 ID 字符串。
|
||||
*
|
||||
* @return 用户 ID 字符串
|
||||
*/
|
||||
public static String getUserIdStr() {
|
||||
return Convert.toStr(getExtra(USER_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户名。
|
||||
*
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUsername() {
|
||||
return Convert.toStr(getExtra(USER_NAME_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户部门 ID。
|
||||
*
|
||||
* @return 部门 ID
|
||||
*/
|
||||
public static Long getDeptId() {
|
||||
return Convert.toLong(getExtra(DEPT_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户部门名称。
|
||||
*
|
||||
* @return 部门名称
|
||||
*/
|
||||
public static String getDeptName() {
|
||||
return Convert.toStr(getExtra(DEPT_NAME_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户部门类别编码。
|
||||
*
|
||||
* @return 部门类别编码
|
||||
*/
|
||||
public static String getDeptCategory() {
|
||||
return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 Token 的扩展信息
|
||||
*
|
||||
* @param key 键值
|
||||
* @return 对应的扩展数据
|
||||
*/
|
||||
private static Object getExtra(String key) {
|
||||
try {
|
||||
return StpUtil.getExtra(key);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户类型。
|
||||
*
|
||||
* @return 用户类型
|
||||
*/
|
||||
public static UserType getUserType() {
|
||||
String loginType = StpUtil.getLoginIdAsString();
|
||||
return UserType.getUserType(loginType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为超级管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 是否为超级管理员
|
||||
*/
|
||||
public static boolean isSuperAdmin(Long userId) {
|
||||
return SystemConstants.SUPER_ADMIN_USER_ID.equals(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为超级管理员
|
||||
*
|
||||
* @return 是否为超级管理员
|
||||
*/
|
||||
public static boolean isSuperAdmin() {
|
||||
return isSuperAdmin(getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否已登录
|
||||
*
|
||||
* @return 是否已登录
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
return StpUtil.isLogin();
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.dromara.common.satoken.config.SaTokenConfig
|
||||
@@ -0,0 +1,13 @@
|
||||
# 内置配置 不允许修改 如需修改请在 nacos 上写相同配置覆盖
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
# 允许动态设置 token 有效期
|
||||
dynamic-active-timeout: true
|
||||
# 允许从 请求参数 读取 token
|
||||
is-read-body: true
|
||||
# 允许从 header 读取 token
|
||||
is-read-header: true
|
||||
# 关闭 cookie 鉴权 从根源杜绝 csrf 漏洞风险
|
||||
is-read-cookie: false
|
||||
# token前缀
|
||||
token-prefix: "Bearer"
|
||||
Reference in New Issue
Block a user