如何在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
相关产品推荐
相关产品推荐

