Nginx文件下载预览加权限验证的思考和实现

news2024/10/5 22:37:50

做的项目中多个模块涉及到附件、图片、PDF/Excel等文件的处理,包括预览、导出和下载等功能。对于体积较小的文件,可以直接由后端以流形式传输给前端处理;而较大的文件则需要通过nginx进行转发。但是如果nginx中不设置鉴权服务,可能会造成数据泄露的风险。为保障数据安全,有必要在nginx中加上鉴权功能,对文件传输和访问进行控制。

先来说思路,为nginx增加ngx_http_auth_request_module模块,实现基于子请求的结果的客户端的授权。如果子请求返回2xx响应码,则允许访问。如果它返回401或403,则访问被拒绝并显示相应的错误代码。子请求返回的任何其他响应代码都被认为是错误的。即在原来的基础上加了一层后端鉴权服务。

1. 下载扩展鉴权模块ngx_http_auth_request_module
git clone https://github.com/PiotrSikora/ngx_http_auth_request_module.git
查看nginx编译安装时安装了哪些模块

image.png
通过在nginx目录下执行:./nginx -V,可以看出编译安装使用了--prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module

2. 安装新模块ngx_http_auth_request_module

进入nginx的源码包(一般编译安装完nginx后会删除,如果本地没有了的话就去官网下个源码包),加入需要安装的模块,重新编译,在编译参数最后添加 --add-module=/usr/local/ngx_http_auth_request_module

# ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --add-module=/usr/local/ngx_http_auth_request_module
# make    // 这里不要make install,不然就覆盖了原来的nginx!!!

注意:这里只需要make就行了,千万不要make install,不然就覆盖了原来的nginx!!!

3. 修改nginx配置
# Managed static resources
server {
    listen       15550;

    location /zcauth {
        internal;
		# 鉴权服务器的地址
        proxy_pass http://127.0.0.1:8081/auth/token;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
    }

    # ROOT for ALL Files(数据文件根路径)
    location / {
        auth_request /zcauth;
		auth_request_set $auth_status $upstream_status;

        root /home/Filedata/;

        #add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods *;
        add_header Access-Control-Allow-Headers *;

        #add_header Content-Type "application/octet-stream";
        autoindex on;
    }
}

在需要请求身份验证的位置,指定auth_request指令,在该指令中指定将授权子请求转发到的内部位置/zcauth,在这里,对于每个请求,都会向内部/zcauth位置发出一个子请求。在/zcauth内的proxy_pass指令,将把身份验证子请求代理到身份验证(鉴权)服务器或服务。由于身份验证子请求将丢弃请求体,因此需要将proxy-pass-request-body指令设置为off,并将Content-Length头设置为空字符串。使用带有proxy_set_header指令的参数传递完整的原始请求URI。(作为选择,可以使用auth_request_set指令根据子请求的结果设置变量值)。

4. 实现后端鉴权服务
package com.yorma.staticauth.controller;

import cn.hutool.core.date.DateUtil;
import com.yorma.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;

import static cn.hutool.core.text.CharSequenceUtil.isBlank;
import static cn.hutool.core.text.CharSequenceUtil.isNotBlank;
import static cn.hutool.core.util.ObjectUtil.isEmpty;
import static com.yorma.staticauth.domain.Const.*;

/**
 * auth验证
 *
 * @author ZHANGCHAO
 * @version 1.0.0
 * @date 2023/7/17 11:34
 */
@Slf4j
@RestController
@RequestMapping("/auth")
public class NginxAuthRequestController {
    
 	public static final String HEADER_TOKEN = "X-Access-Token";
    public static final String USERNAME = "username";
    public static final String PREFIX_USER_TOKEN_INFO = "prefix_user_info_token_";
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/token")
    public void auth(HttpServletRequest request, HttpServletResponse response) {
        // 通过 HttpServletRequest 获取请求头中的 token
        String token = request.getHeader(HEADER_TOKEN);
        log.info("Token from HttpServletRequest: " + token);
        String uri = request.getHeader("X-Original-URI");
        log.info("URI from HttpServletRequest: " + uri);
        if (isBlank(token)) {
            if (isNotBlank(uri) && uri.contains("X-Access-Token=")) {
                String tokens = uri.split("X-Access-Token=")[1];
                String match = MD5Util.MD5Encode(DateUtil.formatDate(new Date()) + "F70C5833-7D02-47A6-B8D5-93B97CBAF87F", "utf-8");
                log.info("本地MD5后的值: " + match + " | 过来的值:" + tokens);
                if (match.equals(tokens)) {
                    response.setStatus(200);
                    return;
                }
            }
            response.setStatus(401);
            return;
        }
        String username = "";
        if (redisTemplate.hasKey(PREFIX_USER_TOKEN_INFO + token)) {
            HashMap<String, Object> claim = (HashMap<String, Object>) redisTemplate.opsForValue().get(PREFIX_USER_TOKEN_INFO + token);
            if (isEmpty(claim)) {
                response.setStatus(403);
                return;
            }
            username = String.valueOf(claim.get(USERNAME));
        }
        if (isBlank(username)) {
            response.setStatus(403);
            return;
        }
        response.setStatus(200);
    }
}

支持两种方式:1. token放到请求头Header里的正常token 2. 直接放请求URL中的按一定规则生成的32位MD5 token。先取Header里的,如果取不到则取URL中的。

5. 测试效果

先来个导出Excel功能,token在请求头中,功能正常。
image.png

表单图片,token在请求URL中,预览正常。
image.png

如果直接在浏览器请求,则失败:
在这里插入图片描述

总结

Nginx文件服务鉴权使用ngx_http_auth_request_module模块实现,功能简单实用。当然还有其他的技术方案可以来实现,如第三方的扩展模块x-sendfile等。可根据自己项目的实际情况灵活处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/782950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

帖子列表和SerializerMixin注意事项

帖子序列化 继承SerializerMixin 即可调用to_dict()序列化 后端 class PostModel(db.Model, SerializerMixin):serialize_only ("id", "title", "content", "create_time", "board", "author")__tablename__ …

Ubuntu22.04上部署Lua开发环境

需求背景 想在Ubuntu22.04上搭建一下Lua的开发环境&#xff0c;其实步骤比较简单的&#xff0c;此文章也适用于Ubuntu主机环境搭建Lua,如果想在在Ubuntu内部署一个容器&#xff0c;然后在容器内搭建Lua的环境&#xff0c;可以先参考容器的创建过程 ubuntu22.04上如何创建有pri…

android adb命令获取处于当前屏幕的Activity

android adb命令获取处于当前屏幕的Activity 使用adb命令&#xff1a; adb shell dumpsys activity activities 输出&#xff0c;例如: ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): * Task{38ef601 #5281 typ…

C++-----list

本期我们来讲解list&#xff0c;有了string和vector的基础&#xff0c;我们学习起来会快很多 目录 list介绍 ​编辑 list常用接口 insert erase reverse sort merge unique remove splice 模拟实现 基础框架 构造函数 push_back 迭代器 常见问题 const迭代器 …

BEVPoolv2 A Cutting-edge Implementation of BEVDet Toward Deployment 论文学习

Github Repo: https://github.com/HuangJunJie2017/BEVDet/tree/dev2.0 Arxiv Paper: https://arxiv.org/abs/2211.17111 1. 解决了什么问题&#xff1f; 多相机 3D 目标检测是自动驾驶领域的基本任务&#xff0c;受到学术界和工业界的大量关注。Lift-Splat-Shoot view trans…

Linux基础IO(一)

Linux基础IO 文章目录 Linux基础IOC语言中的文件操作c语言文件打开方式C语言文件操作函数 系统文件操作stdin/stdout/stderropeanclosewriteread 文件描述符重定向什么是重定向dup2 C语言中的文件操作 我们通过两个代码复习一下C语言中的文件操作&#xff1a; int main() {FI…

uniapp-开发APP使用自定义插件

uniapp-开发APP使用自定义插件 需求背景&#xff1a; 项目组开发了一个APP需要使用到打印机的功能、所以需要通过打印机厂商提供的jdk包结合自己的业务融合到uniapp 中。 首先你需要一个懂开发android开发的同事、让他帮忙配合写一些调用方法&#xff08;调用打印机提供的一些…

态势标绘专题介绍

介绍 这个专栏是专门针对基于Cesium来实现态势标绘的专题专栏,专栏主要实现了30余种态势几何形状的标绘和编辑、文本的标绘和编辑、图片的标绘和编辑以及简单模型的标绘,同时支持标绘结果的导出以及导入。包括最终编写成的一个完整的Vue3.2+TS+Cesium1.107.2的标绘组件。专栏…

【STL】list用法试做_底层实现

目录 一&#xff0c;list 使用 1. list 文档介绍 2. 常见接口 1. list中的sort 2. list sort 与 vector sort效率对比 3. 关于迭代器失效 4. clear 二&#xff0c;list 实现 1.框架搭建 2. 迭代器类——核心框架 3. operator-> 实现 4. const——迭代…

c++11 标准模板(STL)(std::basic_filebuf)(二)

定义于头文件 <fstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_filebuf : public std::basic_streambuf<CharT, Traits> std::basic_filebuf 是关联字符序列为文件的 std::basic_streambuf 。输入序…

基于AOP实现登录日志和操作日志(新手入门版)

基于AOP实现登录日志和操作日志 目录结构代码PostMan测试代码控制台查看输出解析成JSON如果你觉得对你有帮助的话&#xff0c;请点赞收藏 目录结构 代码 package com.demo.mymaintest.constants;import java.lang.annotation.Documented; import java.lang.annotation.ElementT…

在Debian 12 上安装 PHP 5.6, 7.4

环境&#xff1a;Debian 12 Debian 12 默认的PHP版本为 8.2 如果直接安装php7.4就出现下面的报错&#xff1a; sudo apt-get install libapache2-mod-php7.4 php7.4 php7.4-gd php7.4-opcache php7.4-mbstring php7.4-xml php7.4-json php7.4-zip php7.4-curl php7.4-imap p…

【人工智能】神经网络、前向传播、反向传播、梯度下降、局部最小值、多层前馈网络、缓解过拟合的策略

神经网络、前向传播、反向传播 文章目录 神经网络、前向传播、反向传播前向传播反向传播梯度下降局部最小值多层前馈网络表示能力多层前馈网络局限缓解过拟合的策略前向传播是指将输入数据从输入层开始经过一系列的权重矩阵和激活函数的计算后,最终得到输出结果的过程。在前向…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(六)

UIAbility组件间交互&#xff08;设备内&#xff09; UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启…

unity 调用C++追踪物体后的位姿通过 dll,左右手坐标转化2

外参矩阵转四元数,左右手坐标系转化1_天人合一peng的博客-CSDN博客 在之前1的基础上更新 通过定位已经求得了物体的4*4的位姿矩阵&#xff0c;将其变化为四元数并从opengl的右手坐标转化为unity的左手坐标系。 body2world_pose().matrix()为计算得到的4*4的位姿矩阵 例 body…

Go语言之结构体

在实际开发中&#xff0c;我们可以将一组类型不同的、但是用来描述同一件事物的变量放到结构体中。例如&#xff0c;在校学生有姓名、年龄、身高、成绩等属性&#xff0c;学了结构体后&#xff0c;我们就不需要再定义多个变量了&#xff0c;将它们都放到结构体中即可。 在Go语言…

基于linux下的高并发服务器开发(第三章)- 3.7 线程属性

int pthread_attr_init(pthread_attr_t *attr);- 初始化线程属性变量int pthread_attr_destroy(pthread_attr_t *attr);- 释放线程属性的资源int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);- 获取线程分离的状态属性int pthread_attr_setdet…

【Redis】所以延迟双删有啥用

文章目录 1、何为延时双删2、常用缓存策略2.1、介绍2.2、先删缓存后更库2.3、先更库后删缓存2.4、使用场景 3、延时双删实现4、为什么要使用延时双删5、方案选择6、延时双删真的完美吗7、如何确定延时的时间 1、何为延时双删 延迟双删&#xff08;Delay Double Delete&#xf…

Jupyter 安装、简单操作及工作路径更换

一、Jupyter下载安装 pip install jupyterAnaconda是Python另一个非常流行的发行版&#xff0c;它之后有着自己的叫做“conda”的安装工具。用户可以使用它来安装很多第三方包。然而&#xff0c;Anaconda会预装很多包&#xff0c;包括了Jupyter Notebook,所以若已经安装了Anac…

VUE3 语法教程

vue3 起步 刚开始学习 Vue&#xff0c;我们不推荐使用 vue-cli 命令行工具来创建项目&#xff0c;更简单的方式是直接在页面引入 vue.global.js 文件来测试学习。 Vue3 中的应用是通过使用 createApp 函数来创建的&#xff0c;语法格式如下&#xff1a; const app Vue.crea…