Nestjs中如何让JwtStrategy的validate方法获取WebSocket请求上下文
实现方案
默认的AuthGuard('jwt')和JwtStrategy仅适配HTTP请求场景,要支持WebSocket场景下获取上下文、挂载用户payload,可按以下步骤改造:
步骤1:重写JwtAuthGuard适配双协议
重写守卫的getRequest方法,让其同时支持识别HTTP和WebSocket上下文,并且适配WebSocket专属的异常类型:
import { ExecutionContext, Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { WsException } from '@nestjs/websockets'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { // 适配不同上下文的请求对象提取逻辑 getRequest(context: ExecutionContext) { // HTTP场景直接返回原生请求 if (context.getType() === 'http') { return context.switchToHttp().getRequest(); } // WebSocket场景返回握手信息对象,Passport会将其识别为请求载体 if (context.getType() === 'ws') { return context.switchToWs().getClient().handshake; } throw new Error('不支持的上下文类型'); } // 适配WebSocket场景的验证错误抛出逻辑 handleRequest(err, user, info) { if (err || !user) { throw err || new WsException('身份验证失败'); } return user; } }
步骤2:改造JwtStrategy支持上下文获取
开启passReqToCallback配置让validate方法可以拿到上下文对象,同时自定义token提取逻辑适配WebSocket场景:
@Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private authConfigService: AuthConfigService) { super({ // 自定义token提取规则,同时适配HTTP和WebSocket jwtFromRequest: (request) => { // HTTP场景从Authorization头提取Bearer token if (request.headers?.authorization) { return ExtractJwt.fromAuthHeaderAsBearerToken()(request); } // WebSocket场景从握手请求的query参数提取token(也可根据前端传参方式调整为header取值) if (request.query?.token) { return request.query.token; } return null; }, ignoreExpiration: false, secretOrKey: authConfigService.JWT_SECRET, // 开启该配置后,validate方法第一个参数会传入前面提取的请求上下文对象 passReqToCallback: true, }); } async validate(request: any, payload: any) { // WebSocket场景下可以将用户payload挂载到client实例,方便后续业务逻辑访问 if (request.headers?.upgrade === 'websocket') { const wsClient = request.socket; wsClient.user = payload; // 也可根据需求挂载到后续消息体等其他对象上 } return payload; } }
步骤3:网关中使用&获取用户信息
在WebSocket网关直接用@UseGuards(JwtAuthGuard)装饰即可,后续业务逻辑中可以直接拿到挂载的用户信息:
@WebSocketGateway() export class WsGateway { @UseGuards(JwtAuthGuard) @SubscribeMessage('testEvent') handleTestEvent(@Context() ctx: ExecutionContext) { const client = ctx.switchToWs().getClient(); // 直接获取验证通过的用户信息 const currentUser = client.user; // 也可以从handshake对象拿到Passport自动挂载的user属性 const currentUser2 = client.handshake.user; return { event: 'testRes', data: '操作成功' }; } }
注意事项
- 前端建立WebSocket连接时,需要将token放在握手请求的query参数或者请求头中(部分浏览器对WebSocket自定义header有限制,用query参数兼容性更好)
- 如果不需要兼容HTTP场景,可以删掉策略中HTTP相关的token提取逻辑,仅保留WebSocket适配逻辑即可
内容的提问来源于stack exchange,提问作者Vahid Najafi
相关产品推荐
相关产品推荐

