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

NestJS中Token与HttpOnly Cookie授权对比及实现咨询

一、两种方案优劣对比

1. 前端存储Bearer Token(localStorage/sessionStorage/内存)

  • 优势:
    • 跨域场景适配简单,无需额外配置Cookie跨域规则;
    • 前端完全控制Token的存储、携带逻辑,灵活度高。
  • 劣势:
    • 易受XSS攻击,恶意脚本可通过JS读取到Token并窃取;
    • 每次请求需手动在请求头添加 Authorization: Bearer <token>,增加前端代码工作量。

2. HttpOnly Cookie存储Token

  • 优势:
    • 安全性更高:HttpOnly标记的Cookie无法通过JS读取,从根源避免XSS攻击窃取Token;
    • 浏览器自动携带Cookie,前端无需手动处理请求头,减少重复代码。
  • 劣势:
    • 跨域场景需额外配置:后端要设置Cookie的Domain、Path、SameSite属性,前端请求需开启withCredentials;
    • 存在CSRF攻击风险,需配合CSRF Token等防护手段。

二、方案选择建议

  • 若前后端同域部署:优先选择HttpOnly Cookie方案,安全性更优;
  • 若为跨域场景:
    • 可以选择HttpOnly Cookie + 配置CORS跨域规则(开启withCredentials、设置SameSite: 'Lax'或'None');
    • 或采用混合方案:HttpOnly Cookie存Refresh Token,前端内存中存Access Token(减少暴露时间),同时做好XSS防护(比如内容安全策略CSP)。

三、NestJS中实现HttpOnly Cookie方案的步骤

基于你提供的现有代码,做如下修改:

不再返回access_token,改为在响应中设置Cookie:

import {
    Body,
    Controller,
    Get,
    HttpCode,
    HttpStatus,
    Post,
    Request,
    UseGuards,
    Res // 新增导入Res
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from './auth.guard';
import { Response } from 'express'; // 导入express的Response类型

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @HttpCode(HttpStatus.OK)
  @Post('login')
  async signIn(
    @Body() signInDto: Record<string, any>,
    @Res({ passthrough: true }) res: Response // 注入Response,passthrough: true表示不接管响应
  ) {
    // 调用AuthService生成Token
    const { access_token } = await this.authService.signIn(signInDto.username, signInDto.password);
    
    // 设置HttpOnly Cookie
    res.cookie('access_token', access_token, {
      httpOnly: true, // 核心:标记为HttpOnly
      secure: process.env.NODE_ENV === 'production', // 生产环境开启(HTTPS下生效)
      sameSite: 'lax', // 防止CSRF,根据场景可选'lax'/'strict'/'none'
      maxAge: 3600000, // Token有效期,比如1小时(毫秒)
      path: '/' // Cookie生效路径
    });
    
    // 可以返回用户信息,不需要返回Token
    return { message: '登录成功' };
  }

  @UseGuards(AuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

2. 修改AuthGuard,从Cookie中提取Token

不再从Authorization头提取,改为读取Cookie:

import {
    CanActivate,
    ExecutionContext,
    Injectable,
    UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    // 从Cookie提取Token
    const token = request.cookies?.access_token;
    if (!token) {
      throw new UnauthorizedException();
    }
    try {
      const payload = await this.jwtService.verifyAsync(
        token,
        {
          secret: jwtConstants.secret
        }
      );
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }
}

3. (可选)CSRF防护配置

使用HttpOnly Cookie时,需添加CSRF防护。NestJS中可以通过@nestjs/csurf包实现:

  1. 安装依赖:
npm install @nestjs/csurf
  1. 在AppModule中配置:
import { Module } from '@nestjs/common';
import { CsurfModule } from '@nestjs/csurf';

@Module({
  imports: [
    CsurfModule.forRoot({
      cookie: {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax'
      }
    })
  ]
})
export class AppModule {}
  1. 在需要防护的接口中,前端需携带CSRF Token(从Cookie的_csrf中读取,放到请求头XSRF-TOKEN中)。

四、注意事项

  • 生产环境务必开启secure: true,确保Cookie仅在HTTPS下传输;
  • 根据跨域场景调整SameSite属性:跨域时设为'none',同时需开启secure;同域场景设为'lax'或'strict';
  • 如果使用Refresh Token,同样建议存储在HttpOnly Cookie中,避免前端暴露。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 22:14:55