Nginx 结合服务端实现下载文件鉴权

1,355 阅读2分钟

背景

通过 URL 在浏览器下载文件时,并且要判断有权限的用户才能下载。

功能描述

浏览器输入下载 URL,吊起浏览器自带的用户名和密码输入框,将填入的用户信息传给服务端,校验通过下载文件,校验不通过重新输入。

流程

效果视频

20240708174139_rec_.gif

实现思路

  1. 在 Nginx 中配置下载路径拦截
  2. 定义一个需要认证才能访问的文件下载路径
    1. 设置 auth_request 向服务端转发请求,验证用户信息
    2. 设置 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 写的服务端

  1. app/router.js 中添加对应的鉴权路由
import { Application } from 'egg';

export default (app: Application) => {
  const { controller, router } = app;
  
  router.get('auth/protected-check', controller.auth.protectedCheck);
}
  1. 在对应的 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"');
       }
  }