init
This commit is contained in:
32
ruoyi-component/ruoyi-component-satoken/pom.xml
Normal file
32
ruoyi-component/ruoyi-component-satoken/pom.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-component</artifactId>
|
||||
<version>4.8.2</version>
|
||||
</parent>
|
||||
<artifactId>ruoyi-component-satoken</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-component-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-component-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 整合 jwt -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-jwt</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ruoyi.component.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 com.ruoyi.component.satoken.core.PlusSaTokenDao;
|
||||
import com.ruoyi.component.satoken.core.SaPermissionImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* sa-token 配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.ruoyi.component.satoken.core;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import com.ruoyi.component.redis.util.RedisUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class PlusSaTokenDao implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* 获取Value,如无返空
|
||||
*/
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return RedisUtils.getCacheObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入Value,并设定存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 判断是否为永不过期
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
RedisUtils.setCacheObject(key, value);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修修改指定key-value键值对 (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
long expire = getTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.set(key, value, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Value
|
||||
*/
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
RedisUtils.deleteObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Value的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
long timeout = RedisUtils.getTimeToLive(key);
|
||||
return timeout < 0 ? timeout : timeout / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改Value的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getTimeout(key);
|
||||
if (expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.set(key, this.get(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取Object,如无返空
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return RedisUtils.getCacheObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入Object,并设定存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
if (timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
// 判断是否为永不过期
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
RedisUtils.setCacheObject(key, object);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Object (过期时间不变)
|
||||
*/
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
long expire = getObjectTimeout(key);
|
||||
// -2 = 无此键
|
||||
if (expire == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
this.setObject(key, object, expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Object
|
||||
*/
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
RedisUtils.deleteObject(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Object的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
long timeout = RedisUtils.getTimeToLive(key);
|
||||
return timeout < 0 ? timeout : timeout / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改Object的剩余存活时间 (单位: 秒)
|
||||
*/
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
// 判断是否想要设置为永久
|
||||
if (timeout == SaTokenDao.NEVER_EXPIRE) {
|
||||
long expire = getObjectTimeout(key);
|
||||
if (expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
// 如果其已经被设置为永久,则不作任何处理
|
||||
} else {
|
||||
// 如果尚未被设置为永久,那么再次set一次
|
||||
this.setObject(key, this.getObject(key), timeout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RedisUtils.expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 搜索数据
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ruoyi.component.satoken.core;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import com.ruoyi.component.core.domain.model.LoginUser;
|
||||
import com.ruoyi.component.core.enums.UserType;
|
||||
import com.ruoyi.component.satoken.utils.LoginHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* sa-token 权限管理实现类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class SaPermissionImpl implements StpInterface {
|
||||
|
||||
/**
|
||||
* 获取菜单权限列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
return new ArrayList<>(loginUser.getMenuPermission());
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||
if (userType == UserType.SYS_USER) {
|
||||
return new ArrayList<>(loginUser.getRolePermission());
|
||||
} else if (userType == UserType.APP_USER) {
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.ruoyi.component.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.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.component.core.constant.UserConstants;
|
||||
import com.ruoyi.component.core.domain.model.LoginUser;
|
||||
import com.ruoyi.component.core.enums.DeviceType;
|
||||
import com.ruoyi.component.core.enums.UserType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
* <p>
|
||||
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||
* <p>
|
||||
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Slf4j
|
||||
public class LoginHelper {
|
||||
|
||||
public static final String LOGIN_USER_KEY = "loginUser";
|
||||
public static final String USER_KEY = "userId";
|
||||
|
||||
public static final String TENANT_KEY = "tenantId";
|
||||
|
||||
|
||||
/**
|
||||
* 获取租户ID
|
||||
*/
|
||||
public static String getTenantId() {
|
||||
return Convert.toStr(getExtra(TENANT_KEY));
|
||||
}
|
||||
|
||||
private static Object getExtra(String key) {
|
||||
return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
|
||||
}
|
||||
|
||||
public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
|
||||
try {
|
||||
Object obj = SaHolder.getStorage().get(key);
|
||||
if (ObjectUtil.isNull(obj)) {
|
||||
obj = handle.get();
|
||||
SaHolder.getStorage().set(key, obj);
|
||||
}
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 登录系统
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void login(LoginUser loginUser) {
|
||||
loginByDevice(loginUser, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录系统 基于 设备类型
|
||||
* 针对相同用户体系不同设备
|
||||
*
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
||||
SaStorage storage = SaHolder.getStorage();
|
||||
storage.set(LOGIN_USER_KEY, loginUser);
|
||||
storage.set(USER_KEY, loginUser.getUserId());
|
||||
SaLoginModel model = new SaLoginModel();
|
||||
if (ObjectUtil.isNotNull(deviceType)) {
|
||||
model.setDevice(deviceType.getDevice());
|
||||
}
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
// UserType userType = UserType.getUserType(loginUser.getUserType());
|
||||
// if (userType == UserType.SYS_USER) {
|
||||
// model.setTimeout(86400).setActiveTimeout(1800);
|
||||
// } else if (userType == UserType.APP_USER) {
|
||||
// model.setTimeout(86400).setActiveTimeout(1800);
|
||||
// }
|
||||
StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
|
||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
public static void logoutApp(Long userId){
|
||||
try {
|
||||
StpUtil.logout(UserType.APP_USER.getUserType() + ":" + userId);
|
||||
}catch (Exception e){
|
||||
log.error("强制T人下线失败! userId={}",userId,e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户(多级缓存)
|
||||
*/
|
||||
public static LoginUser getLoginUser() {
|
||||
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
|
||||
if (loginUser != null) {
|
||||
return loginUser;
|
||||
}
|
||||
SaSession session = StpUtil.getTokenSession();
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
loginUser = (LoginUser) session.get(LOGIN_USER_KEY);
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
public static LoginUser getLoginUser(String token) {
|
||||
SaSession session = StpUtil.getTokenSessionByToken(token);
|
||||
if (ObjectUtil.isNull(session)) {
|
||||
return null;
|
||||
}
|
||||
return (LoginUser) session.get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
*/
|
||||
public static Long getDeptId() {
|
||||
return getLoginUser().getDeptId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户账户
|
||||
*/
|
||||
public static String getUsername() {
|
||||
return getLoginUser().getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户类型
|
||||
*/
|
||||
public static UserType getUserType() {
|
||||
String loginType = StpUtil.getLoginIdAsString();
|
||||
return UserType.getUserType(loginType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isAdmin(Long userId) {
|
||||
return UserConstants.ADMIN_ID.equals(userId);
|
||||
}
|
||||
|
||||
public static boolean isAdmin() {
|
||||
return isAdmin(getUserId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.ruoyi.component.satoken.config.SaTokenConfig
|
||||
@@ -0,0 +1,13 @@
|
||||
# 内置配置 不允许修改
|
||||
# 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