useGenericAuth未按预期注入用户上下文,如何在GraphQL解析器中获取登录用户?
如何在GraphQL Mesh网关后的GraphQL服务器解析器中获取当前登录用户
问题背景
后端采用TypeScript+GraphQL技术栈,所有请求发往/graphql_gateway网关,通过GraphQL Mesh拼接Supabase与/graphql_server的Schema。已配置@envelop/generic-auth插件解析Supabase用户,但graphql_server的解析器中无法通过context.currentUser获取登录用户。
核心问题分析
- 网关解析的用户上下文未传递至下游
graphql_server - 原代码中从
Authorization头解析Cookie的逻辑不符合标准(Cookie应从Cookie请求头获取) - 下游服务器未正确接收并注入用户信息到解析器上下文
解决方案步骤
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
相关产品推荐
相关产品推荐

