NESTJS 网关/Websocket - 如何通过 socket.emit 发送 jwt access_token
Posted
技术标签:
【中文标题】NESTJS 网关/Websocket - 如何通过 socket.emit 发送 jwt access_token【英文标题】:NESTJS Gateway / Websocket - how to send jwt access_token through socket.emit 【发布时间】:2020-02-28 10:03:48 【问题描述】:我正在为我的项目使用默认护照 jwt AuthGuard。这适用于我的帖子并在设置身份验证标头时获得路由。
现在我想在客户端使用 Nestjs 网关和 socket.io,但我不知道如何将 access_token 发送到网关?
这基本上是我的网关:
@WebSocketGateway()
export class UserGateway
entityManager = getManager();
@UseGuards(AuthGuard('jwt'))
@SubscribeMessage('getUserList')
async handleMessage(client: any, payload: any)
const results = await this.entityManager.find(UserEntity);
console.log(results);
return this.entityToClientUser(results);
在客户端我这样发送:
this.socket.emit('getUserList', users =>
console.log(users);
this.userListSub.next(users);
);
如何以及在何处添加 jwt access_token?对于 Websockets,nestjs 的文档完全忽略了这一点。他们只是说,Guards 对 websockets 的工作与他们对 post/get 等的工作完全相同。见here
【问题讨论】:
【参考方案1】:对于任何寻求解决方案的人。这里是:
@UseGuards(WsGuard)
@SubscribeMessage('yourRoute')
async saveUser(socket: Socket, data: any)
let auth_token = socket.handshake.headers.authorization;
// get the token itself without "Bearer"
auth_token = auth_token.split(' ')[1];
在客户端,您可以像这样添加授权标头:
this.socketOptions =
transportOptions:
polling:
extraHeaders:
Authorization: 'your token', //'Bearer h93t4293t49jt34j9rferek...'
;
...
this.socket = io.connect('http://localhost:4200/', this.socketOptions);
...
之后,您可以访问每个请求服务器端的令牌,如示例中所示。
这里也是我实现的WsGuard
。
@Injectable()
export class WsGuard implements CanActivate
constructor(private userService: UserService)
canActivate(
context: any,
): boolean | any | Promise<boolean | any> | Observable<boolean | any>
const bearerToken = context.args[0].handshake.headers.authorization.split(' ')[1];
try
const decoded = jwt.verify(bearerToken, jwtConstants.secret) as any;
return new Promise((resolve, reject) =>
return this.userService.findByUsername(decoded.username).then(user =>
if (user)
resolve(user);
else
reject(false);
);
);
catch (ex)
console.log(ex);
return false;
我只是检查我是否可以使用我的用户服务从我的数据库中的解码令牌中找到具有用户名的用户。我相信你可以让这个实现更简洁,但它确实有效。
【讨论】:
您好,我也遇到了同样的问题,您能再添加一些代码吗? 在初始化套接字本身时,您必须将令牌添加到客户端的套接字连接中。在那里你可以设置标题。我为客户端编辑了答案。 谢谢!我现在遇到了守卫问题,你自己实现了 WsGuard 吗? 嗨,没问题!WsGuard
是我用CanActivate
实现的。我也在答案中添加了我的守卫实现:)
如果token解码正确,说明已经被服务器签名。重新检查用户是否真的在数据库中是不是有点矫枉过正?由于服务器首先对其进行了签名。【参考方案2】:
虽然问题已得到解答,但我想指出 Guard 无法用于防止未经授权的用户建立连接。
它只用于保护特定事件。
使用@WebSocketGateway
注释的类的handleConnection
方法在您的Guard 的canActivate
之前调用。
我最终在我的网关类中使用了类似的东西:
async handleConnection(client: Socket)
const payload = this.authService.verify(
client.handshake.headers.authorization,
);
const user = await this.usersService.findOne(payload.userId);
!user && client.disconnect();
【讨论】:
【参考方案3】:谢谢!最后,我实现了一个类似于 jwt 守卫的守卫将用户放入请求中。最后我使用来自套接字客户端的查询字符串方法来传递身份验证令牌这是我的实现:
import CanActivate, ExecutionContext, Injectable, Logger from '@nestjs/common';
import WsException from '@nestjs/websockets';
import Socket from 'socket.io';
import AuthService from '../auth/auth.service';
import User from '../auth/entity/user.entity';
@Injectable()
export class WsJwtGuard implements CanActivate
private logger: Logger = new Logger(WsJwtGuard.name);
constructor(private authService: AuthService)
async canActivate(context: ExecutionContext): Promise<boolean>
try
const client: Socket = context.switchToWs().getClient<Socket>();
const authToken: string = client.handshake?.query?.token;
const user: User = await this.authService.verifyUser(authToken);
client.join(`house_$user?.house?.id`);
context.switchToHttp().getRequest().user = user
return Boolean(user);
catch (err)
throw new WsException(err.message);
【讨论】:
在查询字符串(= URL)中传递令牌不是很糟糕吗?这很可能会泄漏。以上是关于NESTJS 网关/Websocket - 如何通过 socket.emit 发送 jwt access_token的主要内容,如果未能解决你的问题,请参考以下文章
NestJs Websocket,如何测试和调试“Socket hang up”?