NESTJS网关/ Websocket-如何通过socket.emit发送jwt access_token

时间:2019-11-02 11:17:23

标签: websocket socket.io nestjs

我正在为我的项目使用默认的护照jwt AuthGuard。这对我的帖子有效,并且在设置身份验证标头时获取路由很好。

现在我想同时在客户端和socket.io一起使用Nestjs网关,但是我不知道如何将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? nestjs的文档完全没有提到Websockets。他们所说的只是,Guards在websocket中的工作方式与在post / get等方面的工作方式完全相同。请参见here

3 个答案:

答案 0 :(得分:6)

在回答问题时,我想指出 Guard 不能用于防止未经授权的用户建立连接。

仅用于保护特定事件。

使用 handleConnection 注释的类的 @WebSocketGateway 方法在您的 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();
  }

答案 1 :(得分:5)

任何寻求解决方案的人。在这里:

  @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;
    }
  }
}

我只是检查是否可以通过用户服务从数据库中的已解码令牌中找到具有用户名的用户。我相信您可以使该实现更简洁,但是可以。

答案 2 :(得分:5)

谢谢!最后,我实现了一个警卫,就像jwt警卫一样,将用户放入请求中。最后,我使用套接字客户端的查询字符串方法传递auth令牌,这是我的实现:

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);
    }
  }
}