如何用Node.js+Express实现带参数校验的隐式文件强制下载?
实现带参数校验的隐藏文件下载(Express)
核心实现方案(重定向隐藏链接)
用户访问/param?key=xxx时,后端先校验密钥合法性,通过后直接重定向到内部下载路由,但给下载路由加防护,禁止直接访问。
后端代码示例
const express = require('express'); const app = express(); const path = require('path'); // 密钥校验逻辑(替换成你的实际校验规则,比如从数据库查询) const isValidKey = (key) => { return key === '23232323'; }; // 对外的参数校验入口 app.get('/param', (req, res) => { const { key } = req.query; if (!isValidKey(key)) { return res.status(403).send('无效访问密钥'); } // 重定向到内部下载路由,用户看不到这个跳转地址 res.redirect('/download/image.zip'); }); // 受保护的内部下载路由 app.get('/download/image.zip', (req, res) => { // 校验请求来源,只允许从/param路由重定向过来 const referer = req.headers.referer; if (!referer || !referer.includes('your-domain.com/param')) { return res.status(403).send('禁止直接访问'); } // 设置下载响应头,触发浏览器下载 res.setHeader('Content-Disposition', 'attachment; filename="image.zip"'); // 返回文件 res.sendFile(path.resolve(__dirname, 'assets/image.zip')); }); app.listen(3000, () => console.log('服务运行在3000端口'));
其他可选实现思路
思路1:直接流式返回文件(无跳转)
校验通过后不做重定向,直接将文件流返回给客户端,全程不会暴露任何下载链接:
app.get('/param', (req, res) => { const { key } = req.query; if (!isValidKey(key)) { return res.status(403).send('无效访问密钥'); } // 直接设置下载头并返回文件 res.setHeader('Content-Disposition', 'attachment; filename="image.zip"'); res.sendFile(path.resolve(__dirname, 'assets/image.zip')); });
这种方式更简洁,用户的请求始终停留在/param?key=xxx,完全看不到实际文件路径。
思路2:生成一次性临时令牌
如果需要支持有限次数/有效期的下载,可生成临时令牌:
- 校验密钥通过后,生成一个带有效期的临时令牌(存Redis或内存)
- 返回自动触发下载的页面,用令牌请求下载接口
- 下载接口校验令牌有效性,通过则返回文件
// 内存存储临时令牌(生产环境建议用Redis) const tempTokens = new Map(); app.get('/param', (req, res) => { const { key } = req.query; if (!isValidKey(key)) { return res.status(403).send('无效访问密钥'); } // 生成临时令牌,有效期10分钟 const token = Math.random().toString(36).slice(2); tempTokens.set(token, { expires: Date.now() + 10 * 60 * 1000 }); // 返回自动跳转的HTML res.send(` <html> <body> <script>window.location.href = '/download?token=${token}';</script> </body> </html> `); }); app.get('/download', (req, res) => { const { token } = req.query; const tokenData = tempTokens.get(token); // 校验令牌有效性 if (!tokenData || tokenData.expires < Date.now()) { tempTokens.delete(token); return res.status(403).send('令牌无效或已过期'); } tempTokens.delete(token); // 令牌一次性使用 res.setHeader('Content-Disposition', 'attachment; filename="image.zip"'); res.sendFile(path.resolve(__dirname, 'assets/image.zip')); });
生产环境注意事项
- 密钥校验逻辑要严谨,避免硬编码,建议从数据库或配置中心读取有效密钥
- 大文件下载建议用
res.download()或流式传输,防止内存溢出 - 内部路由的防护逻辑要完善,避免用户通过猜测路径直接访问文件
内容的提问来源于stack exchange,提问作者kar psk
相关产品推荐
相关产品推荐

