mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-17 05:43:39 +00:00
v3.0.0 init
This commit is contained in:
@@ -4,11 +4,13 @@ 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.ruoyi.common.core.factory.YmlPropertySourceFactory;
|
||||
import org.ruoyi.common.satoken.core.dao.PlusSaTokenDao;
|
||||
import org.ruoyi.common.satoken.core.service.SaPermissionImpl;
|
||||
import org.ruoyi.common.satoken.handler.SaTokenExceptionHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
/**
|
||||
* sa-token 配置
|
||||
@@ -16,7 +18,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
@PropertySource(value = "classpath:common-satoken.yml", factory = YmlPropertySourceFactory.class)
|
||||
public class SaTokenConfig {
|
||||
|
||||
@Bean
|
||||
public StpLogic getStpLogicJwt() {
|
||||
@@ -40,4 +43,12 @@ public class SaTokenConfig implements WebMvcConfigurer {
|
||||
return new PlusSaTokenDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public SaTokenExceptionHandler saTokenExceptionHandler() {
|
||||
return new SaTokenExceptionHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,27 +1,44 @@
|
||||
package org.ruoyi.common.satoken.core.dao;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
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.ruoyi.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 SaTokenDao {
|
||||
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 RedisUtils.getCacheObject(key);
|
||||
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||
return (String) o;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,6 +55,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
} else {
|
||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||
}
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,12 +63,10 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, value, true);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +74,9 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
RedisUtils.deleteObject(key);
|
||||
if (RedisUtils.deleteObject(key)) {
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +85,8 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
long timeout = RedisUtils.getTimeToLive(key);
|
||||
return timeout < 0 ? timeout : timeout / 1000;
|
||||
// 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿
|
||||
return timeout < 0 ? timeout : timeout / 1000 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,17 +94,6 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if (expire == NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
@@ -95,7 +103,21 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return RedisUtils.getCacheObject(key);
|
||||
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Object (指定反序列化类型),如无返空
|
||||
*
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
@SuppressWarnings("unchecked cast")
|
||||
@Override
|
||||
public <T> T getObject(String key, Class<T> classType) {
|
||||
Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +134,7 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
} else {
|
||||
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||
}
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,12 +142,10 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
if (RedisUtils.hasKey(key)) {
|
||||
RedisUtils.setCacheObject(key, object, true);
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +153,9 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
RedisUtils.deleteObject(key);
|
||||
if (RedisUtils.deleteObject(key)) {
|
||||
CAFFEINE.invalidate(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +164,8 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
long timeout = RedisUtils.getTimeToLive(key);
|
||||
return timeout < 0 ? timeout : timeout / 1000;
|
||||
// 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿
|
||||
return timeout < 0 ? timeout : timeout / 1000 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,28 +173,20 @@ public class PlusSaTokenDao implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
if (expire == NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
String keyStr = prefix + "*" + keyword + "*";
|
||||
return (List<String>) CAFFEINE.get(keyStr, k -> {
|
||||
Collection<String> keys = RedisUtils.keys(keyStr);
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package org.ruoyi.common.satoken.core.service;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.enums.UserType;
|
||||
import org.ruoyi.common.core.exception.ServiceException;
|
||||
import org.ruoyi.common.core.service.PermissionService;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -21,13 +27,25 @@ public class SaPermissionImpl implements StpInterface {
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) {
|
||||
PermissionService permissionService = getPermissionService();
|
||||
if (ObjectUtil.isNotNull(permissionService)) {
|
||||
List<String> list = StringUtils.splitList(loginId.toString(), ":");
|
||||
return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1))));
|
||||
} else {
|
||||
throw new ServiceException("PermissionService 实现类不存在");
|
||||
}
|
||||
}
|
||||
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
return new ArrayList<>(loginUser.getMenuPermission());
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
if (userType == UserType.APP_USER) {
|
||||
// 其他端 自行根据业务编写
|
||||
}
|
||||
return new ArrayList<>();
|
||||
if (CollUtil.isNotEmpty(loginUser.getMenuPermission())) {
|
||||
// SYS_USER 默认返回权限
|
||||
return new ArrayList<>(loginUser.getMenuPermission());
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,12 +54,33 @@ public class SaPermissionImpl implements StpInterface {
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) {
|
||||
PermissionService permissionService = getPermissionService();
|
||||
if (ObjectUtil.isNotNull(permissionService)) {
|
||||
List<String> list = StringUtils.splitList(loginId.toString(), ":");
|
||||
return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1))));
|
||||
} else {
|
||||
throw new ServiceException("PermissionService 实现类不存在");
|
||||
}
|
||||
}
|
||||
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
return new ArrayList<>(loginUser.getRolePermission());
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
if (userType == UserType.APP_USER) {
|
||||
// 其他端 自行根据业务编写
|
||||
}
|
||||
return new ArrayList<>();
|
||||
if (CollUtil.isNotEmpty(loginUser.getRolePermission())) {
|
||||
// SYS_USER 默认返回权限
|
||||
return new ArrayList<>(loginUser.getRolePermission());
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private PermissionService getPermissionService() {
|
||||
try {
|
||||
return SpringUtils.getBean(PermissionService.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.ruoyi.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.ruoyi.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 {
|
||||
|
||||
/**
|
||||
* 权限码异常
|
||||
*/
|
||||
@ExceptionHandler(NotPermissionException.class)
|
||||
public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限异常
|
||||
*/
|
||||
@ExceptionHandler(NotRoleException.class)
|
||||
public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证失败
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package org.ruoyi.common.satoken.listener;
|
||||
|
||||
import cn.dev33.satoken.config.SaTokenConfig;
|
||||
import cn.dev33.satoken.listener.SaTokenListener;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.core.constant.CacheConstants;
|
||||
import org.ruoyi.common.core.domain.dto.UserOnlineDTO;
|
||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.enums.UserType;
|
||||
import org.ruoyi.common.core.utils.ServletUtils;
|
||||
import org.ruoyi.common.redis.utils.RedisUtils;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 用户行为 侦听器的实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UserActionListener implements SaTokenListener {
|
||||
|
||||
private final SaTokenConfig tokenConfig;
|
||||
|
||||
/**
|
||||
* 每次登录时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
||||
UserType userType = UserType.getUserType(loginId.toString());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String ip = ServletUtils.getClientIP();
|
||||
LoginUser user = LoginHelper.getLoginUser();
|
||||
UserOnlineDTO dto = new UserOnlineDTO();
|
||||
dto.setIpaddr(ip);
|
||||
// dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
dto.setBrowser(userAgent.getBrowser().getName());
|
||||
dto.setOs(userAgent.getOs().getName());
|
||||
dto.setLoginTime(System.currentTimeMillis());
|
||||
dto.setTokenId(tokenValue);
|
||||
dto.setUserName(user.getUsername());
|
||||
dto.setDeptName(user.getDeptName());
|
||||
if (tokenConfig.getTimeout() == -1) {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
|
||||
}
|
||||
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
// app端 自行根据业务编写
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次注销时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogout(String loginType, Object loginId, String tokenValue) {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被踢下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doKickout(String loginType, Object loginId, String tokenValue) {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被顶下线时触发
|
||||
*/
|
||||
@Override
|
||||
public void doReplaced(String loginType, Object loginId, String tokenValue) {
|
||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被封禁时触发
|
||||
*/
|
||||
@Override
|
||||
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次被解封时触发
|
||||
*/
|
||||
@Override
|
||||
public void doUntieDisable(String loginType, Object loginId, String service) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次打开二级认证时触发
|
||||
*/
|
||||
@Override
|
||||
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doCloseSafe(String loginType, String tokenValue, String service) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doCreateSession(String id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次注销Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doLogoutSession(String id) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次Token续期时触发
|
||||
*/
|
||||
@Override
|
||||
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,21 @@
|
||||
package org.ruoyi.common.satoken.utils;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.core.constant.SystemConstants;
|
||||
import org.ruoyi.common.core.constant.TenantConstants;
|
||||
import org.ruoyi.common.core.constant.UserConstants;
|
||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.enums.DeviceType;
|
||||
import org.ruoyi.common.core.enums.UserType;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
* <p>
|
||||
@@ -30,62 +28,54 @@ import java.util.Set;
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LoginHelper {
|
||||
|
||||
public static final String LOGIN_USER_KEY = "loginUser";
|
||||
public static final String TENANT_KEY = "tenantId";
|
||||
public static final String USER_KEY = "userId";
|
||||
|
||||
/**
|
||||
* 登录系统
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void login(LoginUser loginUser) {
|
||||
loginByDevice(loginUser, null);
|
||||
}
|
||||
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";
|
||||
|
||||
/**
|
||||
* 登录系统 基于 设备类型
|
||||
* 针对相同用户体系不同设备
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
* @param model 配置参数
|
||||
*/
|
||||
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
||||
SaStorage storage = SaHolder.getStorage();
|
||||
storage.set(LOGIN_USER_KEY, loginUser);
|
||||
storage.set(TENANT_KEY, loginUser.getTenantId());
|
||||
storage.set(USER_KEY, loginUser.getUserId());
|
||||
SaLoginModel model = new SaLoginModel();
|
||||
if (ObjectUtil.isNotNull(deviceType)) {
|
||||
model.setDevice(deviceType.getDevice());
|
||||
}
|
||||
public static void login(LoginUser loginUser, SaLoginParameter model) {
|
||||
model = ObjectUtil.defaultIfNull(model, new SaLoginParameter());
|
||||
StpUtil.login(loginUser.getLoginId(),
|
||||
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
||||
.setExtra(USER_KEY, loginUser.getUserId()));
|
||||
model.setExtra(TENANT_KEY, loginUser.getTenantId())
|
||||
.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户(多级缓存)
|
||||
*/
|
||||
public static LoginUser getLoginUser() {
|
||||
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
|
||||
if (loginUser != null) {
|
||||
return loginUser;
|
||||
@SuppressWarnings("unchecked cast")
|
||||
public static <T extends LoginUser> T getLoginUser() {
|
||||
SaSession session = StpUtil.getTokenSession();
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
SaSession tokenSession = StpUtil.getTokenSession();
|
||||
if (tokenSession != null) {
|
||||
loginUser = (LoginUser) tokenSession.get(LOGIN_USER_KEY);
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
;
|
||||
return loginUser;
|
||||
return (T) session.get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
@SuppressWarnings("unchecked cast")
|
||||
public static <T extends LoginUser> T getLoginUser(String token) {
|
||||
SaSession session = StpUtil.getTokenSessionByToken(token);
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
@@ -98,56 +88,71 @@ public class LoginHelper {
|
||||
* 获取用户id
|
||||
*/
|
||||
public static Long getUserId() {
|
||||
Long userId;
|
||||
try {
|
||||
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
|
||||
SaHolder.getStorage().set(USER_KEY, userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return userId;
|
||||
return Convert.toLong(getExtra(USER_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户ID
|
||||
* 获取用户id
|
||||
*/
|
||||
public static String getTenantId() {
|
||||
String tenantId;
|
||||
try {
|
||||
tenantId = (String) SaHolder.getStorage().get(TENANT_KEY);
|
||||
if (ObjectUtil.isNull(tenantId)) {
|
||||
tenantId = (String) StpUtil.getExtra(TENANT_KEY);
|
||||
SaHolder.getStorage().set(TENANT_KEY, tenantId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
*/
|
||||
public static Long getDeptId() {
|
||||
return getLoginUser().getDeptId();
|
||||
public static String getUserIdStr() {
|
||||
return Convert.toStr(getExtra(USER_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
*/
|
||||
public static String getUsername() {
|
||||
return getLoginUser().getUsername();
|
||||
return Convert.toStr(getExtra(USER_NAME_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户ID
|
||||
*/
|
||||
public static String getTenantId() {
|
||||
return Convert.toStr(getExtra(TENANT_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
*/
|
||||
public static Long getDeptId() {
|
||||
return Convert.toLong(getExtra(DEPT_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门名
|
||||
*/
|
||||
public static String getDeptName() {
|
||||
return Convert.toStr(getExtra(DEPT_NAME_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门类别编码
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户类型
|
||||
*/
|
||||
public static UserType getUserType() {
|
||||
String loginId = StpUtil.getLoginIdAsString();
|
||||
return UserType.getUserType(loginId);
|
||||
String loginType = StpUtil.getLoginIdAsString();
|
||||
return UserType.getUserType(loginType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,25 +162,42 @@ public class LoginHelper {
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSuperAdmin(Long userId) {
|
||||
return UserConstants.SUPER_ADMIN_ID.equals(userId);
|
||||
}
|
||||
|
||||
public static boolean isSuperAdmin() {
|
||||
return isSuperAdmin(getUserId());
|
||||
return SystemConstants.SUPER_ADMIN_ID.equals(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为超级管理员
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isSuperAdmin() {
|
||||
return isSuperAdmin(getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为租户管理员
|
||||
*
|
||||
* @param rolePermission 角色权限标识组
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isTenantAdmin(Set<String> rolePermission) {
|
||||
if (CollUtil.isEmpty(rolePermission)) {
|
||||
return false;
|
||||
}
|
||||
return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为租户管理员
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isTenantAdmin() {
|
||||
return isTenantAdmin(getLoginUser().getRolePermission());
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (loginUser == null) {
|
||||
return false;
|
||||
}
|
||||
return Convert.toBool(isTenantAdmin(loginUser.getRolePermission()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,9 +207,11 @@ public class LoginHelper {
|
||||
*/
|
||||
public static boolean isLogin() {
|
||||
try {
|
||||
return getLoginUser() != null;
|
||||
StpUtil.checkLogin();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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