You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ExpressJS的JWT保护路由中使用GraphiQL?

解决受JWT保护的GraphQL路由下GraphiQL正常工作的方案

以下几个方案可以替代ModHeader扩展,让你在路由受保护时正常使用GraphiQL并浏览Schema:

方案1:给GraphiQL页面添加令牌输入与自动携带功能

通过自定义GraphiQL的HTML模板,添加一个令牌输入框,将令牌存入本地存储,之后所有GraphiQL的查询请求都会自动带上这个令牌。同时调整验证中间件,允许GET请求(加载GraphiQL界面)无需令牌,仅拦截POST查询请求。

示例代码:

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const jwt = require('jsonwebtoken');
const schema = require('./schema');

const app = express();

// 验证JWT的中间件
const authenticateToken = (req, res, next) => {
  // 开发环境下,允许GET请求加载GraphiQL界面
  if (process.env.NODE_ENV === 'development' && req.method === 'GET' && req.path === '/graphql') {
    return next();
  }

  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) return res.sendStatus(401);

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
};

app.use('/graphql', authenticateToken);

app.use('/graphql', graphqlHTTP({
  schema: schema,
  graphiql: {
    headerEditorEnabled: true,
    // 自定义模板添加令牌输入区域
    template: (graphiql) => `
      <!DOCTYPE html>
      <html>
        <head>
          <title>GraphiQL</title>
          <style>
            .token-bar {
              padding: 10px;
              background: #f5f5f5;
              border-bottom: 1px solid #e0e0e0;
            }
            #token-input {
              padding: 8px;
              width: 350px;
              margin-right: 8px;
            }
            #save-token {
              padding: 8px 16px;
              cursor: pointer;
            }
          </style>
          ${graphiql.css}
        </head>
        <body>
          <div class="token-bar">
            <input type="text" id="token-input" placeholder="输入JWT令牌(格式:Bearer xxx)">
            <button id="save-token">保存令牌</button>
          </div>
          ${graphiql.html}
          <script>
            const tokenInput = document.getElementById('token-input');
            const saveBtn = document.getElementById('save-token');
            const originalFetch = window.graphiqlFetch;

            // 覆盖fetch方法,自动携带令牌
            window.graphiqlFetch = async (url, options) => {
              const savedToken = localStorage.getItem('graphql-auth-token');
              if (savedToken) {
                options.headers = {
                  ...options.headers,
                  'Authorization': savedToken
                };
              }
              return originalFetch(url, options);
            };

            // 保存令牌到本地存储
            saveBtn.addEventListener('click', () => {
              const token = tokenInput.value.trim();
              if (token) {
                localStorage.setItem('graphql-auth-token', token);
                alert('令牌已保存,查询时会自动携带');
              }
            });

            // 页面加载时恢复已保存的令牌
            window.onload = () => {
              const token = localStorage.getItem('graphql-auth-token');
              if (token) tokenInput.value = token;
            };
          </script>
          ${graphiql.js}
        </body>
      </html>
    `
  },
  context: ({ req }) => ({
    user: req.user
  })
}));

app.listen(4000, () => console.log('服务器运行在http://localhost:4000/graphql'));

方案2:放宽GraphiQL界面的访问限制,手动添加请求头

仅在开发环境中,让GET请求(加载GraphiQL界面)跳过JWT验证,POST请求(实际查询)仍需验证。然后利用GraphiQL自带的headerEditorEnabled功能,手动在头部编辑器中添加Authorization头。

示例代码:

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const jwt = require('jsonwebtoken');
const schema = require('./schema');

const app = express();

const authenticateToken = (req, res, next) => {
  // 开发环境下允许GET请求访问GraphiQL界面
  if (process.env.NODE_ENV === 'development' && req.method === 'GET') {
    return next();
  }

  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) return res.sendStatus(401);

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
};

app.use('/graphql', authenticateToken, graphqlHTTP({
  schema: schema,
  graphiql: {
    headerEditorEnabled: true,
    defaultQuery: `# 请在顶部"Headers"编辑器中添加:
# Authorization: Bearer <你的JWT令牌>

query {
  currentUser {
    id
    username
  }
}`
  },
  context: ({ req }) => ({
    user: req.user
  })
}));

app.listen(4000);

注意事项

  • 以上方案仅适合开发环境使用,生产环境必须严格限制GraphiQL的访问,或对所有请求(包括GET)进行完整的JWT验证。
  • 方案1中本地存储的令牌仅在当前浏览器有效,关闭浏览器后不会丢失(除非手动清除),不会影响其他网站的请求。

内容的提问来源于stack exchange,提问作者Dac0d3r

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:05:31