基于Ruoyi的同一token跨系统访问,后端单点登录并且鉴权方案
基于Ruoyi的同一token跨系统访问,后端单点登录并且鉴权方案
需求场景以及先决条件
- 同一环境下的多个ruoyi项目,各自使用相同的一组用户(我这里用的是LDAP的登录,不影响本文),但是每个权限拥有各自项目的权限.
- 希望一个前端登录的token,可以跨越不同的后端同时使用,即一个token访问所有系统,每个系统的权限,系统内部自行判断.
默认方案
- 默认情况下,ruoyi框架的设计是使用redis存储一个uuid作为token的key,用户信息作为value,存入到redis中的,例如
login_tokens:27e3c1ed-dee7-4495-99c7-d175beb4f0b1
{“@type”:“com.ruoyi.framework.security.LoginUser”,“browser”:“Firefox 13”,“deptId”:100,“expireTime”:1731988245275,“ipaddr”:“127.0.0.1”,“loginLocation”:“内网IP”,“loginTime”:1731984645275,“os”:“Windows 10”,“permissions”:Set[“:😗”],“token”:“27e3c1ed-dee7-4495-99c7-d175beb4f0b1”,“…省略以下”}
- 且每一个项目使用一个redis的db,互相独立
- 前端持有的jwt,是以uuid生成的令牌,即
Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjI3ZTNjMWVkLWRlZTctNDQ5NS05OWM3LWQxNzViZWI0ZjBiMSJ9.Ec0a7hRsyAL2ehZfiXWox0aczb58rKpARVipWfhbuir6sPrNsCArbbF47zDA2Mtmf1CHBdyodJx1bsWZrjLiKw
等价于
login_tokens:27e3c1ed-dee7-4495-99c7-d175beb4f0b1
改造思路
- 使用一个db,例如db0,所有的项目都多建立一个redis连接,连到db0.实现redis的部分共享
- 读取到token存在,即认为用户已登录
- 读取对应的程序的token,如果不存在,就是系统内部登录,创建一个对应token.
- 示例如下,指示一个token: 27e… 同时登录到了report和spc_mini2个系统,第一个key存储的值是username,其他的都是自己用的user信息,即权限信息等; 3和7是各自系统的其他redis缓存
![在这里插入图片描述]()
改造代码,一共4个类需要变更

- 新建数据源RedisCacheUser
- 配置数据源在RedisConfig
@Bean
@SuppressWarnings(value = {
"unchecked", "rawtypes" })
public RedisTemplate<String, Object> redisTemplateDb0(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 使用原工厂配置,创建一个新的,但是指向其他的db
LettuceConnectionFactory originalLettuceFactory = (LettuceConnectionFactory) connectionFactory;
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(originalLettuceFactory.getHostName());
redisStandaloneConfiguration.setPort(originalLettuceFactory.getPort());
redisStandaloneConfiguration.setPassword(originalLettuceFactory.getPassword());
LettuceConnectionFactory clonedFactory = new LettuceConnectionFactory(redisStandaloneConfiguration);
clonedFactory.setDatabase(0);
clonedFactory.afterPropertiesSet();
template.setConnectionFactory(clonedFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
- 修改登录和获取用户的方式,就是存入到redis的key
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
if (user == null)
{
String userKeyUser = getTokenUser(uuid);
String username = redisCache.getCacheObject(userKeyUser);
user = (LoginUser) userDetailsService.loadUserByUsername(username);
user.setToken(uuid);
setUserAgent(user);
refreshToken(user);
}
return user;
} catch (Exception e) {
}
}
return null;
}
完整需要修改的代码
package com.ruoyi.framework.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
/**
* redis配置
*
* @author ruoyi
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@Primary
@SuppressWarnings(value = {
"unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper


浙公网安备 33010602011771号