如何在 Graphql 中删除自省查询的身份验证

Posted

技术标签:

【中文标题】如何在 Graphql 中删除自省查询的身份验证【英文标题】:How to remove authentication for introspection query in Graphql 【发布时间】:2019-10-12 02:53:45 【问题描述】:

所以这可能是一个非常基本的问题,所以请多多包涵。让我解释一下我在做什么以及我真正需要什么。


解释


我使用 ApolloGraphql(apollo-server-express npm 模块)创建了一个 graphql 服务器。

这里是代码 sn-p 给你一个想法。

api.js

import express from 'express'
import rootSchema from './root-schema'
.... // some extra code
app = express.router()
app.use(jwtaAuthenticator) // --> this code authenticates Authorization header
.... // some more middleware's added
const graphQLServer = new ApolloServer(
  schema: rootSchema, // --> this is root schema object
  context: context => context,
  introspection: true, 
)
graphQLServer.applyMiddleware( app, path: '/graphql' )

server.js

import http from 'http'
import express from 'express'
import apiRouter from './api' // --> the above file
const app = express()
app.use([some middlewares])
app.use('/', apiRouter)
....
....
export async function init () 

try 
  const httpServer = http.createServer(app)
  httpServer
    .listen(PORT)
    .on('error', (err) =>  setTimeout(() => process.exit(1), 5000) )
   catch (err) 
    setTimeout(() => process.exit(1), 5000)
  
  console.log('Server started --- ', PORT)

export default app

index.js

require('babel-core')
require('babel-polyfill')
require = require('esm')(module/* , options */)
const server = require('./server.js') // --> the above file

server.init()

问题说明


我正在使用 node index.js 来启动应用程序。因此,应用程序期望 Authorization 标头(JWT 令牌)始终存在,即使对于 introspection 查询也是如此。但这不是我想要的,我希望即使没有令牌,内省查询也可以解决。这样任何人都可以看到文档。

请说明一下,并请指导什么是最好的方法。快乐编码:)

【问题讨论】:

【参考方案1】:

.startsWith('query Introspection')不安全的,因为任何查询都可以命名为 Introspection

更好的方法是检查整个查询。

首先导入graphql并准备自省查询字符串:

const  parse, print, getIntrospectionQuery  = require('graphql');
// format introspection query same way as apollo tooling do
const introspectionQuery = print(parse(getIntrospectionQuery()));

然后在 Apollo Server 配置检查查询:

context: ( req ) => 
  // allow introspection query
  if (req.body.query === introspectionQuery) 
    return ;
  

  // continue

【讨论】:

如果req.body.query 内省查询中的一些小调整与后端生成的inrospectionQuery 略有不同怎么办?或者这不太可能? 如果您在客户端和服务器中使用相同版本的graphql包,那么查询应该没有任何差异。【参考方案2】:

在 GraphQL 中有很多不同的方式来处理授权,如 the docs 所示:

express(或其他一些框架,如hapikoa)添加中间件 检查单个解析器内部的授权 检查数据模型中的授权 使用custom directives

添加快速中间件非常适合防止未经授权访问您的整个架构。如果您想允许对某些字段进行未经身份验证的访问,但不允许其他字段,通常建议您使用上述方法之一将授权逻辑从框架层移动到 GraphQL 或数据模型层。

【讨论】:

谢谢指导,我去看看。【参考方案3】:

所以我终于找到了解决方案,这就是我所做的。

我先告诉你,基本路径上添加了2个中间件。像这样:

app //--> this is express.Router()
  .use(jwtMw) // ---> these are middlewares
  .use(otherMw)

jwtMw检查用户身份验证的,由于即使 introspection 查询也属于此 MW,它曾经验证。所以,经过一番研究,我找到了这个解决方案:

jwtMw.js

function addJWTMeta (req, res, next) 
   // we can check for null OR undefined and all, then check for query Introspection, with better condition like with ignore case
   if (req.body.query.trim().startsWith('query Introspection')) 
      req.isIntrospection = true
      return next()
   
   ...
   ...
   // ---> extra code to do authentication of the USER based on the Authorization header

export default addJWTMeta

otherMw.js

function otherMw (req, res, next) 
  if (req.isIntrospection) return next()
  ...
  ...
  // ---> extra code to do some other context creation

export default otherMw

所以在 jwtMw.js 中,我们正在检查如果 查询是 Introspection,只需在 req 对象中添加一个变量并继续前进,并且在 jwtMw.js 之后的下一个中间件中,任何想要检查自省查询的人只需检查该 变量(在这种情况下是 isIntrospection) 以及它是否存在并且是 true,请继续。我们可以添加此代码并扩展到每个中间件,如果 req.isIntrospection 存在,则继续进行或执行实际处理。

编码愉快:)

【讨论】:

以上是关于如何在 Graphql 中删除自省查询的身份验证的主要内容,如果未能解决你的问题,请参考以下文章

GraphQL 架构文件应包含有效的 GraphQL 自省查询结果

如何使用 GraphQL 构建经过身份验证的查询?

如何使用 graphql 和护照设置身份验证但仍使用 Playground

React Apollo Link - 如何在使用 <Query /> 和 <Mutation /> 组件之外向 GraphQL 服务器查询身份验证令牌?

GraphQL Relay 自省查询失败:“查询”类型上的未知字段“__type”

Apollo GraphQL 客户端查询返回自省结果而不是数据