使用 grunt-connect-proxy 把 /api 转发到后端并附带 JWT
解读
国内前端项目普遍采用“本地 dev-server + 后端联调”模式,grunt-connect-proxy 是 Grunt 生态里最常用的代理插件,用来把本地开发服务器(grunt-contrib-connect)的特定路径转发到真正的后端接口。
面试官问“附带 JWT”,核心想考察两点:
- 能否正确配置代理规则,把
/api开头的请求无缝转发到后端域名; - 能否在转发时动态注入 Authorization 头,而不是把 token 写死在配置文件里,避免泄漏。
现场如果只说“装插件、配 target”只能拿 60 分,必须给出可灰度、可切换、可安全注入的完整方案,才能体现资深工程经验。
知识点
- grunt-connect-proxy 的 context、host、port、changeOrigin、headers、rewrite 等关键字段含义;
- Gruntfile.js 里通过 template 语法读取环境变量或外部文件,实现 token 与代码分离;
- 利用 httpHeaders 选项或 middleware 阶段拦截,在每次请求前动态插入 Authorization;
- 与 grunt-contrib-connect 的 livereload 共存时,代理规则顺序必须放在 livereload 之前,否则会被静态中间件短路;
- 国内常见后端网关要求 X-Forwarded-For 与 X-Real-IP 透传,需在 headers 里补全;
- 安全规范:JWT 不得提交到代码仓库,必须通过 .env.local 或 CI 变量注入,并在 .gitignore 中排除。
答案
- 安装依赖
npm i -D grunt-contrib-connect grunt-connect-proxy dotenv
- 在项目根目录建
.env.local(已加入 .gitignore)
VUE_APP_API_HOST=https://gateway.xxx.com
VUE_APP_API_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
- Gruntfile.js 关键片段
require('dotenv').config({ path: '.env.local' });
module.exports = function(grunt) {
grunt.initConfig({
connect: {
options: {
port: 9000,
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function(connect, options, middlewares) {
// 1. 把代理中间件插到最前面
middlewares.unshift(
require('grunt-connect-proxy/lib/utils').proxyRequest
);
// 2. 继续挂载静态文件与 livereload
middlewares.push(connect.static('.tmp'));
middlewares.push(connect().use(
'/bower_components',
connect.static('./bower_components')
));
middlewares.push(connect.static('app'));
return middlewares;
}
}
},
proxies: [{
context: '/api',
host: process.env.VUE_APP_API_HOST.replace(/^https?:\/\//, ''),
port: 443,
https: true,
changeOrigin: true,
// 3. 动态注入 JWT
headers: {
'Authorization': 'Bearer ' + process.env.VUE_APP_API_JWT,
'X-Real-IP': '127.0.0.1'
},
rewrite: {
'^/api': '/api/v1' // 如果后端统一带版本号
}
}]
},
watch: {
livereload: {
options: { livereload: '<%= connect.options.livereload %>' },
files: ['app/**/*']
}
}
});
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-connect-proxy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('serve', function(target) {
grunt.task.run([
'configureProxies:server',
'connect:livereload',
'watch'
]);
});
};
- 启动
npx grunt serve
此时访问 http://localhost:9000/api/user/info 会被代理到 https://gateway.xxx.com/api/v1/user/info,并自动携带 Authorization: Bearer <JWT>。
拓展思考
- 多环境切换:把
.env.local拆成.env.dev、.env.sit、.env.uat,在 CI 里通过grunt serve --env=sit读取对应文件,实现一键切换后端与 token。 - token 续期:开发阶段 JWT 有效期短,可写一个 grunt-task-jwt-refresh,监听 401 响应,自动调用登录接口刷新 token 并回写
.env.local,避免手动复制。 - 微服务场景:如果
/api/order、/api/pay指向不同域名,可在proxies数组里配多条规则,利用 context 优先级保证匹配顺序;或者引入 http-proxy-middleware 做更细粒度的路径重写。 - 安全加固:生产构建时通过 grunt-replace 把
process.env.*占位符替换成__ENV__标记,由运维在容器启动时利用sed注入真实值,实现配置与制品分离,满足国内金融级交付审计要求。