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

useGenericAuth未按预期注入用户上下文,如何在GraphQL解析器中获取登录用户?

如何在GraphQL Mesh网关后的GraphQL服务器解析器中获取当前登录用户

问题背景

后端采用TypeScript+GraphQL技术栈,所有请求发往/graphql_gateway网关,通过GraphQL Mesh拼接Supabase与/graphql_server的Schema。已配置@envelop/generic-auth插件解析Supabase用户,但graphql_server的解析器中无法通过context.currentUser获取登录用户。

核心问题分析

  1. 网关解析的用户上下文未传递至下游graphql_server
  2. 原代码中从Authorization头解析Cookie的逻辑不符合标准(Cookie应从Cookie请求头获取)
  3. 下游服务器未正确接收并注入用户信息到解析器上下文

解决方案步骤

1. 修正Envelop插件的用户解析逻辑

修改envelopPlugins.ts,从正确的请求头获取Cookie并解析用户:

import { ResolveUserFn, useGenericAuth } from '@envelop/generic-auth'
import { User, createClient } from '@supabase/supabase-js'
import { parse } from 'cookie'
import { IncomingMessage } from 'http'

const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_API_KEY)

type Context = {
  req: IncomingMessage
}

const resolveUserFn: ResolveUserFn<User, Context> = async (context: Context) => {
  // 从Cookie请求头获取Supabase认证令牌
  const cookieHeader = context?.req?.headers?.cookie
  if (!cookieHeader) {
    console.error('Cookie请求头缺失')
    return null
  }
  const cookies = parse(cookieHeader)

  if (!cookies?.['supabase-auth-token']) {
    console.error('Cookie中无Supabase认证令牌')
    return null
  }

  // 解析Supabase令牌数组,提取JWT
  const tokenArray: string[] = JSON.parse(cookies['supabase-auth-token']) as string[]
  const jwt = tokenArray?.[0]
  if (!jwt) {
    console.error('JWT令牌缺失')
    return null
  }

  // 通过Supabase验证JWT并获取用户
  const { data: { user }, error } = await supabase.auth.getUser(jwt)
  if (error) {
    console.error('Supabase认证失败:', error)
    return null
  }

  return user
}

const plugins = [
  useGenericAuth({
    resolveUserFn,
    mode: 'protect-all'
  })
]

export default plugins

2. 配置Mesh网关传递用户上下文到下游服务器

修改.meshrc.yaml中的Server源配置,将网关解析到的用户通过请求头传递给graphql_server:

serve:
  endpoint: /graphql
  playground: true
  port: 3000
sources:
  - name: Supabase
    handler:
      graphql:
        endpoint: '{env.SUPABASE_GRAPHQL_URL}'
        operationHeaders:
          apiKey: '{env.SUPABASE_API_KEY}'
          Content-Type: application/json
        schemaHeaders:
          apiKey: '{env.SUPABASE_API_KEY}'
          Content-Type: application/json
  - name: Server
    handler:
      graphql:
        endpoint: '{env.SERVER_GRAPHQL_URL}'
        source: ./../graphql_server/src/graphql/schema.ts
        # 将网关上下文的currentUser通过请求头传递给下游
        operationHeaders:
          X-User: '{JSON.stringify(context.currentUser)}'
          # 保留原始授权头,用于下游二次验证(可选)
          Authorization: '{context.request.headers.authorization}'
additionalEnvelopPlugins: './src/envelopPlugins'

3. 下游graphql_server注入用户到解析器上下文

修改graphql_server/server.ts,添加钩子获取请求头中的用户信息,并注入Mercurius上下文:

import Fastify from 'fastify'
import mercurius from 'mercurius'
import resolvers from './graphql/resolvers'
import schema from './graphql/schema'
import { User, createClient } from '@supabase/supabase-js'

const ENV = process.env.NODE_ENV || 'development'
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_API_KEY)

const envToLogger = {
  development: {
    transport: {
      target: 'pino-pretty',
      options: {
        translateTime: 'HH:MM:ss Z',
        ignore: 'pid,hostname'
      }
    }
  },
  production: true
}

const app = Fastify({
  logger: envToLogger[ENV]
})

// 预验证钩子:从请求头获取用户信息
app.addHook('preValidation', async (req) => {
  // 方式1:直接解析网关传递的用户对象
  const userHeader = req.headers['x-user']
  if (userHeader) {
    try {
      req.user = JSON.parse(userHeader as string) as User
    } catch (e) {
      req.log.error('解析用户信息失败:', e)
    }
  }

  // 方式2:从Authorization头获取JWT二次验证(更安全)
  // const authHeader = req.headers.authorization
  // if (authHeader?.startsWith('Bearer ')) {
  //   const jwt = authHeader.slice(7)
  //   const { data: { user }, error } = await supabase.auth.getUser(jwt)
  //   if (!error) req.user = user
  // }
})

// 注册Mercurius并注入用户上下文
void app.register(mercurius, {
  schema,
  resolvers,
  graphiql: true,
  context: (req) => ({
    currentUser: req.user
  })
})

export default app

4. 验证解析器中的用户获取

此时graphql_server的解析器即可正常获取用户:

// resolvers.ts
export default {
  Query: {
    testQuery: (parent: unknown, args: unknown, context: any) => {
      console.log('当前登录用户:', context.currentUser)
      return 'test'
    }
  }
}

内容的提问来源于stack exchange,提问作者thomas-woehrle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 22:54:56