基于GRANDstack的问答平台用户级授权实现方案问询
当然有通用解决方案,而且完美适配你基于GRANDstack和graphql-auth-directives搭建的问答平台!核心思路就是结合当前用户身份,在数据查询环节动态过滤符合授权规则的内容,下面我给你一步步拆解具体实现:
一、先明确核心授权规则
在动手前先把规则锚定清楚,避免逻辑混乱:
- 所有已发布的答案(
is_draft: false)对所有登录用户(甚至未登录用户,如果你的平台允许)可见 - 草稿答案(
is_draft: true)仅对创建该草稿的用户本人可见
二、具体实现步骤
1. 确保上下文传递当前用户ID
在Apollo Server的配置中,每次请求的context必须包含当前登录用户的ID(通常从请求头的JWT令牌中解析),这是整个授权逻辑的基础:
const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => { // 这里假设你已经实现了JWT验证逻辑,解析出用户ID const authHeader = req.headers.authorization; const userId = authHeader ? parseJwt(authHeader.split(' ')[1]).userId : null; return { userId, driver: neo4jDriver }; // 同时传入Neo4j驱动 }, });
2. 在Resolver或Schema中添加过滤逻辑
因为GRANDstack基于Neo4j,最直接的方式是通过Cypher查询实现动态过滤,这里提供两种常用方式:
方式一:自定义Resolver实现过滤
修改Question类型的answers字段Resolver,在查询时加入授权条件:
const Question = { answers: async (parent, _, context) => { const { userId, driver } = context; const session = driver.session(); try { const result = await session.run(` MATCH (q:Question {id: $questionId})-[:HAS_ANSWER]->(a:Answer) // 核心过滤条件:已发布的答案,或者是当前用户自己的草稿 WHERE a.is_draft = false OR (a.is_draft = true AND a.createdById = $userId) RETURN a `, { questionId: parent.id, userId: userId || '' // 未登录时userId为空,不会匹配任何草稿 }); return result.records.map(record => record.get('a').properties); } finally { await session.close(); } } };
方式二:用@cypher指令在Schema中定义
如果习惯用neo4j-graphql-js的自动生成能力,可以直接在Schema的Question类型中嵌入Cypher查询,$userId会自动从上下文获取:
type Question { id: ID! title: String! # 用@cypher指令实现带授权过滤的答案查询 answers: [Answer!]! @cypher( statement: """ MATCH (this)-[:HAS_ANSWER]->(a:Answer) WHERE a.is_draft = false OR (a.is_draft = true AND a.createdById = $userId) RETURN a """ ) } type Answer { id: ID! text: String! is_draft: Boolean! createdById: ID! # 必须存储答案创建者的用户ID }
3. 结合graphql-auth-directives做辅助校验(可选)
如果需要确保只有登录用户能查询答案列表,可以给answers字段添加@isAuthenticated指令(这是graphql-auth-directives仓库提供的内置指令):
type Question { id: ID! title: String! answers: [Answer!]! @isAuthenticated @cypher(...) }
三、边界情况验证
- 未登录用户:
userId为null,Cypher条件中不会匹配任何草稿,仅返回已发布的A1、A2 - 用户U2查询:
userId匹配A3的createdById,返回A1、A2、A3 - 其他用户查询:
userId不匹配A3的createdById,仅返回A1、A2
四、为什么这是通用方案?
这套逻辑可以直接扩展到其他用户级授权场景(比如私人草稿文章、专属收藏夹等),核心都是在数据查询阶段结合当前用户身份做条件过滤,完全贴合GRANDstack的技术栈,不需要引入额外的复杂权限框架。
内容的提问来源于stack exchange,提问作者N Meibergen
相关产品推荐
相关产品推荐

