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

NestJS GraphQL订阅在含Guard的Resolver中失效,报logIn未定义错误

NestJS GraphQL订阅鉴权失败问题解决

问题根源

订阅基于WebSocket协议,和HTTP请求的上下文结构完全不同。常规Guard如果只处理HTTP请求的request对象,在订阅场景下会因为获取不到有效上下文而抛出Cannot read properties of undefined (reading 'logIn')错误——本质是Guard中依赖的某个对象(比如HTTP请求实例)未被正确初始化。

解决步骤

1. 修改Guard适配双上下文

更新Guard代码,同时支持HTTP请求和WebSocket订阅的上下文解析,从对应位置提取认证凭证(比如JWT Token):

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthService } from './auth.service'; // 替换为你的认证服务

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const gqlCtx = GqlExecutionContext.create(context).getContext();
    let token: string;

    // 处理HTTP请求(查询/突变)
    if (gqlCtx.req) {
      token = gqlCtx.req.headers.authorization?.split(' ')[1];
    } 
    // 处理WebSocket订阅
    else if (gqlCtx.connectionParams) {
      token = gqlCtx.connectionParams.authorization?.split(' ')[1];
    } 
    // 无有效凭证直接拒绝
    else {
      return false;
    }

    if (!token) return false;

    try {
      // 验证Token并挂载用户信息到上下文
      const user = await this.authService.verifyToken(token);
      gqlCtx.user = user;
      return true;
    } catch (err) {
      return false;
    }
  }
}

2. 客户端传递订阅连接参数

确保客户端在建立WebSocket连接时,将认证凭证放在connectionParams中(以Apollo Client为例):

import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { WebSocketLink } from '@apollo/client/link/ws';

const httpLink = new HttpLink({ uri: 'http://localhost:3000/graphql' });

const wsLink = new WebSocketLink({
  uri: 'ws://localhost:3000/graphql',
  options: {
    reconnect: true,
    // 传递认证Token
    connectionParams: {
      authorization: `Bearer ${localStorage.getItem('your_token_key')}`,
    },
  },
});

// 根据操作类型自动切换HTTP/WSS链路
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});

3. 配置GraphQL模块的订阅上下文

在NestJS的GraphQL模块配置中,确保订阅上下文能正确传递connectionParams:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
      subscriptions: {
        'graphql-ws': {
          path: '/graphql',
          // 可选:提前在连接时做初步校验
          onConnect: (context) => {
            if (!context.connectionParams?.authorization) {
              throw new Error('认证凭证缺失');
            }
            return context;
          },
        },
      },
      // 统一处理HTTP和订阅的上下文
      context: ({ req, connection }) => {
        return connection 
          ? { connectionParams: connection.context } 
          : { req };
      },
    }),
  ],
})
export class AppModule {}

验证

完成上述配置后,重新启动服务,客户端发起订阅请求时会自动携带认证凭证,Guard能正确解析上下文并完成鉴权,订阅即可正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 18:12:48