如何在GraphQL查询中检查权限和其他条件?

时间:2016-05-20 21:49:19

标签: node.js mongoose graphql

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

  • args?怎么会这样呢?
  • resolve()?看看用户是否有权限并以某种方式 消除/改变一些args?

示例:

如果用户是“访客”,他只能看到公开帖子,“管理员”可以看到所有内容。

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)

2 个答案:

答案 0 :(得分:13)

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

  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函数中的上下文,如下所示:

    resolve(parent, args, context){
      if(!context.user.isAdmin){
        args.isPublic = true;
      }
      return PostModel.find(args).exec();
    }
    
  4. 您可以直接在解析函数中进行授权检查,但如果您有模型层,我强烈建议您通过将用户对象传递给模型层来实现它。这样,您的代码将更加模块化,更易于重用,并且您不必担心在某处解析器中忘记某些检查。

    有关授权的更多背景知识,请查看此帖(也由我自己撰写): Auth in GraphQL - part 2

答案 1 :(得分:9)

帮助我们解决公司授权问题的一种方法是将解析器视为中间件的组合。上面的例子非常棒,但是随着你的授权机制变得更加先进,它将会变得非常难以规范。

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

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

ResolverMiddlewareFn是一个获取GraphQLFieldResolver并返回GraphQLFieldResolver的函数。

为了组成我们的解析器中间件函数,我们将使用(你猜对了)compose函数!这是在javascript中实现的example of compose,但您也可以在ramda和其他函数库中找到撰写函数。 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)