You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS中Express-Session+Redis实现记住我功能时Cookie过期值为Null问题

问题:Express-Session + Redis 存储中Cookie的maxAge/expires为null的原因

我基于NestJS开发应用,使用PassportJs、Express-Session并以Redis作为会话存储,目前在实现“记住我”功能时遇到问题:会话已成功存入Redis且无报错,但Redis中存储的Cookie的maxAge和expires均为null。

相关代码

app.module.ts

export class AppModule implements NestModule{
    
constructor(@InjectConnection(ConnectionName.DB_ALPHA_REDIS_CONNECTION_NAME) private readonly connection: Redis) {}
      configure(consumer: MiddlewareConsumer) {
        consumer
          .apply(
            session({
              name: process.env.ALPHA_AUTH_SESSION,
              store: new RedisStore({ client: this.connection }),
              saveUninitialized: false,
              secret: process.env.SESSION_SECRET,
              rolling: false,
              resave: false,
              cookie:{ httpOnly: true, secure: false, sameSite: true }
            }),
            passport.initialize(),
            passport.session(),
          )
          .forRoutes('*');
      }  
    }

local-auth.guard.ts

@Injectable()
export class LocalAuthGuard extends AuthGuard('local'){

    async canActivate(context: ExecutionContext){
       
        const REQUEST = context.switchToHttp().getRequest()
        const RESPONSE = context.switchToHttp().getResponse()

        const BODY = plainToClass(LoginBody, REQUEST.body)

        const ERRORS = await validate(BODY)

        const ERR_MESSAGES = ERRORS.flatMap(({ constraints }) =>
            Object.values(constraints),
        );

        if(ERR_MESSAGES.length > 0)
            RESPONSE.status(404).send(buildResponse(404, 'There is some errors', ERR_MESSAGES))

        REQUEST.sessionStore.ttl = BODY.rememberMe ? 60 * 60 : 60
        REQUEST.session.cookie.maxAge = BODY.rememberMe ? 60 * 60 * 1000 : 60 * 1000

        // 输出结果符合预期:3600
        console.log( REQUEST.sessionStore.ttl) 

        /**
        输出结果符合预期:
        cookie: {
          path: '/',
          _expires: 2023-06-20T00:24:26.712Z,
          originalMaxAge: 3600000,
          httpOnly: true,
          secure: false,
          sameSite: true
        }
        但Redis中存储的cookie.originalMaxAge = null,cookie.expires = null 
        */
        console.log( REQUEST.session.cookie)

        const RESULT = (await super.canActivate(context)) as boolean
        
        await super.logIn(REQUEST)
        return RESULT
    }

}

Redis存储内容

{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":true,"path":"/","sameSite":true},"passport":{"user":{"id":"648a59999f34ed94915ed3ba"}}}

原因分析及解决方案

核心原因

  1. 会话修改时机错误:你在super.canActivate()和super.logIn()之前修改session的cookie配置,此时会话还未完成登录初始化,后续Passport的登录逻辑可能会重置会话的cookie设置,导致你的修改没有被持久化到Redis。
  2. 直接修改sessionStore.ttl无效:RedisStore的ttl参数是在会话中间件初始化时确定的,后续直接修改REQUEST.sessionStore.ttl不会被存储逻辑读取,应该通过cookie的maxAge来间接控制会话过期时间。
  3. 未手动触发会话保存:修改session配置后,没有主动调用session.save(),可能中间件自动保存时使用的是原始的cookie配置。

修复方案

调整代码逻辑,在登录完成后修改session的cookie,并手动触发保存:

@Injectable()
export class LocalAuthGuard extends AuthGuard('local'){

    async canActivate(context: ExecutionContext){
        const REQUEST = context.switchToHttp().getRequest()
        const RESPONSE = context.switchToHttp().getResponse()

        const BODY = plainToClass(LoginBody, REQUEST.body)
        const ERRORS = await validate(BODY)
        const ERR_MESSAGES = ERRORS.flatMap(({ constraints }) => Object.values(constraints));

        if(ERR_MESSAGES.length > 0) {
            RESPONSE.status(404).send(buildResponse(404, 'There is some errors', ERR_MESSAGES))
            return false;
        }

        // 先完成登录流程
        const RESULT = (await super.canActivate(context)) as boolean
        await super.logIn(REQUEST)

        // 登录成功后再修改cookie的过期时间
        const maxAge = BODY.rememberMe ? 60 * 60 * 1000 : 60 * 1000
        REQUEST.session.cookie.maxAge = maxAge

        // 手动保存会话,确保修改同步到Redis
        await new Promise((resolve, reject) => {
            REQUEST.session.save((err) => {
                if(err) reject(err)
                else resolve(null)
            })
        })

        return RESULT
    }

}

额外注意:

  • 移除REQUEST.sessionStore.ttl的修改代码,RedisStore会自动根据cookie.maxAge的值设置会话在Redis中的过期时间(单位为秒,maxAge需转成对应秒数)。
  • 由于配置了saveUninitialized: false,未初始化的会话不会被保存,登录后会话才会被正式初始化,此时修改cookie配置才会生效。

内容的提问来源于stack exchange,提问作者TheMlouden

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:35:10