SaToken接口鉴权
This commit is contained in:
@@ -58,11 +58,11 @@ public class RedisKeyConstants {
|
|||||||
/**
|
/**
|
||||||
* 构建角色权限数据 KEY
|
* 构建角色权限数据 KEY
|
||||||
*
|
*
|
||||||
* @param roleId 角色 ID
|
* @param roleKey 角色 ID
|
||||||
* @return 角色权限数据 KEY
|
* @return 角色权限数据 KEY
|
||||||
*/
|
*/
|
||||||
public static String buildRolePermissionsKey(Long roleId) {
|
public static String buildRolePermissionsKey(String roleKey) {
|
||||||
return ROLE_PERMISSIONS_KEY_PREFIX + roleId;
|
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,7 @@ public interface RoleMapper extends BaseMapper<RoleEntity> {
|
|||||||
*/
|
*/
|
||||||
List<RoleEntity> selectEnabledRoleList();
|
List<RoleEntity> selectEnabledRoleList();
|
||||||
|
|
||||||
|
List<String> selectRoleKeyByUserId(Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -1,9 +1,7 @@
|
|||||||
package top.crushtj.xiaoyi.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -14,6 +12,5 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface UserRoleRelMapper extends BaseMapper<UserRoleRelEntity> {
|
public interface UserRoleRelMapper extends BaseMapper<UserRoleRelEntity> {
|
||||||
|
|
||||||
List<Long> selectByUserId(Long userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,10 @@
|
|||||||
WHERE status = 1
|
WHERE status = 1
|
||||||
AND is_deleted = 0
|
AND is_deleted = 0
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectRoleKeyByUserId" resultType="java.lang.String">
|
||||||
|
SELECT r.role_key
|
||||||
|
FROM t_role r
|
||||||
|
LEFT JOIN t_user_role_rel urr ON r.id = urr.role_id
|
||||||
|
WHERE urr.user_id = #{userId}
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
-3
@@ -10,8 +10,5 @@
|
|||||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<select id="selectByUserId" resultType="java.lang.Long">
|
|
||||||
SELECT role_id FROM t_user_role_rel WHERE user_id = #{userId} AND is_deleted = 0
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
+6
-6
@@ -88,27 +88,27 @@ public class PushRolePermissions2RedisRunner implements ApplicationRunner {
|
|||||||
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
||||||
|
|
||||||
// 构建角色 ID 和权限 DO 列表的映射关系
|
// 构建角色 ID 和权限 DO 列表的映射关系
|
||||||
Map<Long, List<PermissionEntity>> roleIdPermissionMap = new HashMap<>();
|
Map<String, List<String>> roleIdPermissionMap = new HashMap<>();
|
||||||
roleList.forEach(role -> {
|
roleList.forEach(role -> {
|
||||||
Long roleId = role.getId();
|
Long roleId = role.getId();
|
||||||
//获取角色对应的权限id列表
|
//获取角色对应的权限id列表
|
||||||
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
||||||
if (CollUtil.isNotEmpty(permissionIds)) {
|
if (CollUtil.isNotEmpty(permissionIds)) {
|
||||||
List<PermissionEntity> perDOS = Lists.newArrayList();
|
List<String> permissionKeys = Lists.newArrayList();
|
||||||
permissionIds.forEach(permissionId -> {
|
permissionIds.forEach(permissionId -> {
|
||||||
// 根据权限 ID 获取具体的权限 DO 对象
|
// 根据权限 ID 获取具体的权限 DO 对象
|
||||||
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
||||||
if (Objects.nonNull(permissionDO)) {
|
if (Objects.nonNull(permissionDO)) {
|
||||||
perDOS.add(permissionDO);
|
permissionKeys.add(permissionDO.getPermissionKey());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
roleIdPermissionMap.put(roleId, perDOS);
|
roleIdPermissionMap.put(role.getRoleKey(), permissionKeys);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
||||||
roleIdPermissionMap.forEach((roleId, permission) -> {
|
roleIdPermissionMap.forEach((roleKey, permission) -> {
|
||||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
|
String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
.set(key, JsonUtils.toJsonString(permission));
|
.set(key, JsonUtils.toJsonString(permission));
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-5
@@ -18,8 +18,10 @@ import top.crushtj.framework.common.utils.IdGenerator;
|
|||||||
import top.crushtj.framework.common.utils.JsonUtils;
|
import top.crushtj.framework.common.utils.JsonUtils;
|
||||||
import top.crushtj.framework.common.utils.MaskUtils;
|
import top.crushtj.framework.common.utils.MaskUtils;
|
||||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||||
|
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||||
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||||
|
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper;
|
||||||
import top.crushtj.xiaoyi.auth.enums.LoginTypeEnum;
|
import top.crushtj.xiaoyi.auth.enums.LoginTypeEnum;
|
||||||
@@ -55,6 +57,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private UserRoleRelMapper userRoleRelMapper;
|
private UserRoleRelMapper userRoleRelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RoleMapper roleMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TransactionTemplate transactionTemplate;
|
private TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
@@ -154,8 +159,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
userRoleRelMapper.insert(userRoleRel);
|
userRoleRelMapper.insert(userRoleRel);
|
||||||
|
|
||||||
// 将用户角色信息存储到 Redis 中
|
// 将用户角色信息存储到 Redis 中
|
||||||
List<Long> roles = new ArrayList<>();
|
RoleEntity role = roleMapper.selectById(COMMON_USER_ROLE_ID);
|
||||||
roles.add(COMMON_USER_ROLE_ID);
|
List<String> roles = new ArrayList<>(1);
|
||||||
|
roles.add(role.getRoleKey());
|
||||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
@@ -167,7 +173,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
// 回滚 redis 自增ID
|
// 回滚 redis 自增ID
|
||||||
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
||||||
.get(XIAOYI_ID_GENERATOR_KEY))
|
.get(XIAOYI_ID_GENERATOR_KEY))
|
||||||
.toString().trim());
|
.toString()
|
||||||
|
.trim());
|
||||||
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
||||||
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
@@ -187,8 +194,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||||
Boolean hasKey = redisTemplate.hasKey(userRolesKey);
|
Boolean hasKey = redisTemplate.hasKey(userRolesKey);
|
||||||
if (!hasKey) {
|
if (!hasKey) {
|
||||||
List<Long> roles = userRoleRelMapper.selectByUserId(userId);
|
List<String> roles = roleMapper.selectRoleKeyByUserId(userId);
|
||||||
redisTemplate.opsForValue().set(userRolesKey,JsonUtils.toJsonString(roles));
|
redisTemplate.opsForValue()
|
||||||
|
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,18 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson 组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public class SaTokenConfigure {
|
|||||||
.check(r -> StpUtil.checkLogin()) // 校验是否登录
|
.check(r -> StpUtil.checkLogin()) // 校验是否登录
|
||||||
;
|
;
|
||||||
|
|
||||||
SaRouter.match("/auth/user/logout", r -> StpUtil.checkPermission("user"));
|
|
||||||
// 权限认证 -- 不同模块, 校验不同权限
|
// 权限认证 -- 不同模块, 校验不同权限
|
||||||
// SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
|
// SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
|
||||||
// SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
|
// SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
package top.crushtj.xiaoyi.gateway.auth;
|
package top.crushtj.xiaoyi.gateway.auth;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpInterface;
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import top.crushtj.xiaoyi.gateway.constant.RedisKeyConstants;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -17,15 +28,80 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class StpInterfaceImpl implements StpInterface {
|
public class StpInterfaceImpl implements StpInterface {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, String> redisTemplate;
|
||||||
|
@Resource
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||||
log.info("## 获取用户权限列表, loginId: {}", loginId);
|
log.info("## 获取用户权限列表, loginId: {}", loginId);
|
||||||
return List.of();
|
// 构建 用户-角色 Redis Key
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||||
|
|
||||||
|
// 根据用户 ID ,从 Redis 中获取该用户的角色集合
|
||||||
|
String useRolesValue = redisTemplate.opsForValue()
|
||||||
|
.get(userRolesKey);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(useRolesValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 JSON 字符串转换为 List<String> 角色集合
|
||||||
|
List<String> userRoleKeys = objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(userRoleKeys)) {
|
||||||
|
// 查询这些角色对应的权限
|
||||||
|
// 构建 角色-权限 Redis Key 集合
|
||||||
|
List<String> rolePermissionsKeys = userRoleKeys.stream()
|
||||||
|
.map(RedisKeyConstants::buildRolePermissionsKey)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 通过 key 集合批量查询权限,提升查询性能。
|
||||||
|
List<String> rolePermissionsValues = Objects.requireNonNull(redisTemplate.opsForValue()
|
||||||
|
.multiGet(rolePermissionsKeys))
|
||||||
|
.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(rolePermissionsValues)) {
|
||||||
|
List<String> permissions = Lists.newArrayList();
|
||||||
|
|
||||||
|
// 遍历所有角色的权限集合,统一添加到 permissions 集合中
|
||||||
|
rolePermissionsValues.forEach(jsonValue -> {
|
||||||
|
try {
|
||||||
|
// 将 JSON 字符串转换为 List<String> 权限集合
|
||||||
|
List<String> rolePermissions = objectMapper.readValue(jsonValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
permissions.addAll(rolePermissions);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("==> JSON 解析错误: ", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 返回此用户所拥有的权限
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public List<String> getRoleList(Object loginId, String loginType) {
|
public List<String> getRoleList(Object loginId, String loginType) {
|
||||||
log.info("## 获取用户角色列表, loginId: {}", loginId);
|
log.info("## 获取用户角色列表, loginId: {}", loginId);
|
||||||
return List.of();
|
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||||
|
String useRolesValue = redisTemplate.opsForValue()
|
||||||
|
.get(userRolesKey);
|
||||||
|
if (StringUtils.isBlank(useRolesValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 JSON 字符串转换为 List<String> 集合
|
||||||
|
return objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title RedisTemplateConfig
|
||||||
|
* @description Redis 配置类
|
||||||
|
* @date 2026/01/12 19:13
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisTemplateConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(@NonNull RedisConnectionFactory connectionFactory) {
|
||||||
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
|
// 设置 RedisTemplate 的连接工厂
|
||||||
|
redisTemplate.setConnectionFactory(connectionFactory);
|
||||||
|
|
||||||
|
// 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值,确保 key 是可读的字符串
|
||||||
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
|
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||||
|
|
||||||
|
// 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值, 确保存储的是 JSON 格式
|
||||||
|
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
redisTemplate.setValueSerializer(serializer);
|
||||||
|
redisTemplate.setHashValueSerializer(serializer);
|
||||||
|
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title RedisKeyConstants
|
||||||
|
* @date 2026/2/3 15:24
|
||||||
|
* @description Redis key 常量
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RedisKeyConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户对应角色集合 KEY 前缀
|
||||||
|
*/
|
||||||
|
private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色对应的权限集合 KEY 前缀
|
||||||
|
*/
|
||||||
|
private static final String ROLE_PERMISSIONS_KEY_PREFIX = "role:permissions:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建角色对应的权限集合 KEY
|
||||||
|
*
|
||||||
|
* @param roleKey 角色 key
|
||||||
|
* @return 角色对应的权限集合 KEY
|
||||||
|
*/
|
||||||
|
public static String buildRolePermissionsKey(String roleKey) {
|
||||||
|
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用户-角色 KEY
|
||||||
|
*
|
||||||
|
* @param userId 用户 ID
|
||||||
|
* @return 用户-角色 KEY
|
||||||
|
*/
|
||||||
|
public static String buildUserRoleKey(Long userId) {
|
||||||
|
return USER_ROLES_KEY_PREFIX + userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user