如何用RedisJSON替代Redis存储express-session并手动访问会话数据?
问题描述
我想跳过req.session.reload()和req.session.save(),直接手动操作express-session的数据,同时用RedisJSON替代原生Redis存储。但遇到两个问题:
- express-session默认用Redis的
.get()方法存储会话数据,会把数据转成字符串,导致无法用client.json.get()读取 - 虽然我能直接调用
RedisClient.json的方法,但在express-session的存储逻辑里访问这些方法时,结果是undefined
express-session的核心存储逻辑参考:
defineMethod(Session.prototype, 'save', function save(fn) { this.req.sessionStore.set(this.id, this, fn || function(){}); return this; });
我的当前配置代码:
const RedisStore = require("connect-redis").default; const { createClient } = require("redis"); require("dotenv").config(); const RedisClient = createClient({ url: `redis://localhost:${process.env.REDISPORT}` }); RedisClient.connect(); const store = new RedisStore({ client: RedisClient }); const RedisJsonGet = RedisClient.json.get.bind(RedisClient.json); const RedisJsonSet = RedisClient.json.set.bind(RedisClient.json); const sessionmiddleware = session( { store: store, secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: true } )
解决方案
方案一:自定义RedisStore子类,重写核心方法
通过继承connect-redis的RedisStore,重写get/set/destroy方法,直接调用RedisJSON的命令处理会话数据,从根源上替换存储逻辑。
const RedisStore = require("connect-redis").default; const { createClient } = require("redis"); const session = require("express-session"); const crypto = require("crypto"); require("dotenv").config(); const RedisClient = createClient({ url: `redis://localhost:${process.env.REDISPORT}` }); RedisClient.connect(); // 自定义RedisJSON存储类 class RedisJSONStore extends RedisStore { constructor(options) { super(options); this.client = options.client; } // 重写set方法:用RedisJSON存储完整JSON对象 set(id, session, callback) { try { return this.client.json.set(`sess:${id}`, "$", session) .then(() => callback(null)) .catch(err => callback(err)); } catch (err) { callback(err); } } // 重写get方法:用RedisJSON读取JSON对象 get(id, callback) { try { return this.client.json.get(`sess:${id}`) .then(session => { callback(null, session || null); }) .catch(err => callback(err)); } catch (err) { callback(err); } } // 可选:重写destroy方法,用RedisJSON删除会话 destroy(id, callback) { return this.client.json.del(`sess:${id}`) .then(() => callback(null)) .catch(err => callback(err)); } } // 创建自定义存储实例 const store = new RedisJSONStore({ client: RedisClient }); const sessionmiddleware = session({ store: store, secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: true });
这样配置后,express-session会自动用RedisJSON存储数据,你可以直接通过RedisClient.json.get("sess:会话ID")手动读取完整的JSON会话对象。
方案二:包装Redis客户端,拦截默认get/set调用
如果不想自定义Store类,可以直接包装Redis客户端的get/set方法,当操作会话前缀(默认是sess:)的键时,自动切换为RedisJSON命令。
const RedisStore = require("connect-redis").default; const { createClient } = require("redis"); const session = require("express-session"); const crypto = require("crypto"); require("dotenv").config(); const RedisClient = createClient({ url: `redis://localhost:${process.env.REDISPORT}` }); // 保存原生方法 const originalGet = RedisClient.get.bind(RedisClient); const originalSet = RedisClient.set.bind(RedisClient); // 替换get方法:会话键用RedisJSON读取 RedisClient.get = function(key, ...args) { if (key.startsWith("sess:")) { return this.json.get(key, ...args); } return originalGet(key, ...args); }; // 替换set方法:会话键用RedisJSON存储 RedisClient.set = function(key, value, ...args) { if (key.startsWith("sess:")) { // 解析express-session传入的序列化字符串为对象 const sessionObj = typeof value === "string" ? JSON.parse(value) : value; return this.json.set(key, "$", sessionObj, ...args); } return originalSet(key, value, ...args); }; RedisClient.connect(); const store = new RedisStore({ client: RedisClient }); const sessionmiddleware = session({ store: store, secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: true });
注意事项
- 若之前用原生Redis存储过会话,需清空旧数据避免格式冲突
- 确保Redis服务器已安装RedisJSON模块(推荐使用Redis Stack,或Redis 6.2+手动安装模块)
- 手动访问会话时,直接调用
RedisClient.json.get("sess:你的会话ID")即可获取完整JSON对象
内容的提问来源于stack exchange,提问作者Bobthefishmonger
相关产品推荐
相关产品推荐

