【问题标题】:How to check permissions and other conditions in GraphQL query?如何在 GraphQL 查询中检查权限和其他条件?
【发布时间】:2016-09-18 06:57:34
【问题描述】:

我如何检查用户是否有权查看查询某些东西?我不知道该怎么做。

  • args?这怎么行?
  • resolve()?查看用户是否有权限并以某种方式 消除/更改一些参数?

示例:

如果用户是“访问者”,他只能看到公开的帖子,“管理员”可以看到一切。

const userRole = 'admin';  // Let's say this could be "admin" or "visitor"

const Query = new GraphQLObjectType({
    name: 'Query',
    fields: () => {
        return {
            posts: {
                type: new GraphQLList(Post),
                args: {
                    id: {
                        type: GraphQLString
                    },
                    title: {
                        type: GraphQLString
                    },
                    content: {
                        type: GraphQLString
                    },
                    status: {
                        type: GraphQLInt  // 0 means "private", 1 means "public"
                    },
                },

                // MongoDB / Mongoose magic happens here
                resolve(root, args) {
                    return PostModel.find(args).exec()
                }
            }
        }
    }
})

更新 - Mongoose 模型看起来像这样:

import mongoose from 'mongoose'

const postSchema = new mongoose.Schema({
    title: {
        type: String
    },
    content: {
        type: String
    },
    author: {
        type: mongoose.Schema.Types.ObjectId,  // From user model/collection
        ref: 'User'
    },
    date: {
        type: Date,
        default: Date.now
    },
    status: {
        type: Number,
        default: 0    // 0 -> "private", 1 -> "public"
    },
})

export default mongoose.model('Post', postSchema)

【问题讨论】:

    标签: node.js mongoose graphql


    【解决方案1】:

    您可以在解析函数或模型层中检查用户的权限。以下是您必须采取的步骤:

    1. 在执行查询之前验证用户。这取决于您的服务器,通常发生在 graphql 之外,例如通过查看与请求一起发送的 cookie。有关如何使用 Passport.js 执行此操作的更多详细信息,请参阅 this Medium post
    2. 将经过身份验证的用户对象或用户 ID 添加到上下文中。在 express-graphql 中,您可以通过 context 参数来实现:

      app.use('/graphql', (req, res) => {
        graphqlHTTP({ schema: Schema, context: { user: req.user } })(req, res);
      }
      
    3. 像这样在解析函数中使用上下文:

      resolve(parent, args, context){
        if(!context.user.isAdmin){
          args.isPublic = true;
        }
        return PostModel.find(args).exec();
      }
      

    您可以直接在解析函数中进行授权检查,但如果您有模型层,我强烈建议通过将用户对象传递给模型层来实现它。这样一来,您的代码将更加模块化,更易于重用,而且您不必担心在某个地方的解析器中忘记了一些检查。

    有关授权的更多背景信息,请查看这篇文章(也是我自己写的): Auth in GraphQL - part 2

    【讨论】:

    • 如果您使用的是 Mongoose,在解析函数中进行检查是最简单的。我对 Mongoose 不是很熟悉,但是前段时间我写了一个使用 Mongoose 和 SQLite 的 GraphQL 服务器教程。它使用 apolloServer 代替 express-graphql。它看起来有点不同,但是对于解析函数和数据获取来说,一切都差不多。你可以找到它here
    • 是的,您可以返回 null 或抛出错误,GraphQL 将停止向下查询该分支。如果您需要根据数据库中的内容(例如角色或其他内容)检查权限,那么您可以链接承诺。首先获取项目和与项目相关的权限,然后在返回时检查用户是否具有权限。如果用户没有权限,则抛出错误或返回 null。如果用户有权限,则返回该项目(或类似的东西)。
    【解决方案2】:

    帮助我们解决公司授权问题的一种方法是将解析器视为中间件的组合。上面的例子很好,但它会在规模上变得不守规矩,尤其是当你的授权机制变得更先进时。

    作为中间件组合的解析器示例可能如下所示:

    type ResolverMiddlewareFn = 
      (fn: GraphQLFieldResolver) => GraphQLFieldResolver;
    

    ResolverMiddlewareFn 是一个接受 GraphQLFieldResolver 并返回 GraphQLFieldResolver 的函数。

    为了组合我们的解析器中间件函数,我们将使用(你猜对了)组合函数!这是一个用 javascript 实现的 example of compose,但您也可以在 ramda 和其他函数库中找到 compose 函数。 Compose 让我们可以将简单的函数组合成更复杂的函数。

    回到 GraphQL 权限问题,让我们看一个简单的例子。 假设我们要登录解析器,授权用户,然后运行肉和土豆。 Compose 让我们将这三个部分组合在一起,以便我们可以轻松地在我们的应用程序中测试和重用它们。

    const traceResolve =
      (fn: GraphQLFieldResolver) =>
      async (obj: any, args: any, context: any, info: any) => {
        const start = new Date().getTime();
        const result = await fn(obj, args, context, info);
        const end = new Date().getTime();
        console.log(`Resolver took ${end - start} ms`);
        return result;
      };
    
    const isAdminAuthorized =
      (fn: GraphQLFieldResolver) =>
      async (obj: any, args: any, context: any, info: any) => {
        if (!context.user.isAdmin) {
          throw new Error('User lacks admin authorization.');
        }
        return await fn(obj, args, context, info);
      }
    
    const getPost = (obj: any, args: any, context: any, info: any) => {
      return PostModel.find(args).exec();
    }
    
    const getUser = (obj: any, args: any, context: any, info: any) => {
      return UserModel.find(args).exec();
    }
    
    // You can then define field resolve functions like this:
    postResolver: compose(traceResolve, isAdminAuthorized)(getPost)
    
    // And then others like this:
    userResolver: compose(traceResolve, isAdminAuthorized)(getUser)
    

    【讨论】:

      猜你喜欢
      • 2013-09-03
      • 2019-02-20
      • 1970-01-01
      • 1970-01-01
      • 2023-01-25
      • 2011-03-24
      • 2021-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多