背景
通过 URL 在浏览器下载文件时,并且要判断有权限的用户才能下载。
功能描述
浏览器输入下载 URL,吊起浏览器自带的用户名和密码输入框,将填入的用户信息传给服务端,校验通过下载文件,校验不通过重新输入。
效果视频
实现思路
- 在 Nginx 中配置下载路径拦截
- 定义一个需要认证才能访问的文件下载路径
- 设置
auth_request向服务端转发请求,验证用户信息 - 设置
alias文件静态地址,若鉴权通过,下载文件
- 设置
代码
Nginx 配置
nginx.config
location /auth {
proxy_pass http://localhost:7001/auth/protected-check; # 向后端转发的鉴权请求
proxy_redirect off;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 需鉴权的文件下载
location /auth-download {
auth_request /auth;
alias /uploads/email/; # 绝对路径
autoindex on;
sendfile on;
sendfile_max_chunk 1m;
tcp_nopush on;
expires off;
add_header Cache-Control private;
}
服务端接口
这里用的是 egg.js 写的服务端
- 在
app/router.js中添加对应的鉴权路由
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router } = app;
router.get('auth/protected-check', controller.auth.protectedCheck);
}
- 在对应的 Controller 中添加
protectedCheck方法
- 当服务器响应一个 HTTP 401 Unauthorized 状态码,并且在响应头请求响应头中包含
WWW-Authenticate字段时,多数现代浏览器会自动弹出一个对话框要求用户输入用户名和密码 - 最后,根据 Nginx 的配置,如果这个函数返回 200 状态码,Nginx 将认为鉴权已通过,并允许访问配置的文件目录进行下载。否则,Nginx将继续阻止访问,直到鉴权通过。
app/controller/auth.js
public async downloadCheck() {
const { ctx } = this;
const authHeader = ctx.request.header.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
ctx.status = 401;
ctx.set('WWW-Authenticate', 'Basic realm="Restricted Area"');
return;
}
const base64Credentials = authHeader.slice(6);
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
// 这里你可以添加自己的验证逻辑,比如查询数据库
if (username === 'admin' && password === 'password') {
ctx.body = 'Access granted';
// 当 status 为 200 时 Nginx 会判断鉴权通过,进而从配置的文件目录下载文件
ctx.status = 200;
} else {
ctx.status = 401;
ctx.set('WWW-Authenticate', 'Basic realm="Restricted Area"');
}
}