Nginx 静态资源鉴权访问

前言

相信许多的小伙伴使用过Nginx服务器,来代理网站页面或者代理文件资源,配置简单,灵活。但是若出现像带权限的来访问Nginx的静态资源时,那简单的配置将不生效。

原理

Nginx 静态资源鉴权访问

准备

需要用到的知识、工具有

  1. spingBoot

  2. nginx

  3. mysql

  4. 一些文件

开始

Nginx配置方式

我们来拿一个简单的Server的配置举例。

其中会出现几个重要的信息

  1. /resource 下的 internal 属性: 写上这个属性,即代表此前缀请求不对外开放,仅可以内部访问
  2. /file 下的 proxy_pass 指提供文件鉴权服务的地址
	server {
        listen       90; # 监听端口
        server_name  localhost; # 域名

				# 静态资源前缀
        location /resource { # 静态资源访问前缀
            internal; # 内部访问!!!! 此处非常重要
            alias C:/authFile/; # 代理的静态文件目录
        }

        # 鉴权前缀
        location /file {
            proxy_redirect off;
            proxy_set_header Host  $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:6001; # 指定提供文件鉴权服务的地址
        }

	}

Mysql 表配置

ER-图

Nginx 静态资源鉴权访问

SQL脚本

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for auth_file
-- ----------------------------
DROP TABLE IF EXISTS `auth_file`;
CREATE TABLE `auth_file`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `file_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件地址',
  `role_id` int(255) NULL DEFAULT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `ROLE_ID`(`role_id`) USING BTREE,
  CONSTRAINT `ROLE_ID` FOREIGN KEY (`role_id`) REFERENCES `auth_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for auth_role
-- ----------------------------
DROP TABLE IF EXISTS `auth_role`;
CREATE TABLE `auth_role`  (
  `id` int(255) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for auth_user
-- ----------------------------
DROP TABLE IF EXISTS `auth_user`;
CREATE TABLE `auth_user`  (
  `id` int(255) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名称',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for auth_user_role
-- ----------------------------
DROP TABLE IF EXISTS `auth_user_role`;
CREATE TABLE `auth_user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `role_id` int(11) NULL DEFAULT NULL COMMENT '角色ID',
  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户ID',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `U`(`role_id`) USING BTREE,
  INDEX `R`(`user_id`) USING BTREE,
  CONSTRAINT `R` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `U` FOREIGN KEY (`role_id`) REFERENCES `auth_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

文件准备

文件结构图

Nginx 静态资源鉴权访问

与之对应SQL表数据

四张表依次为 文件表、角色表、角色用户表、用户表

Nginx 静态资源鉴权访问Nginx 静态资源鉴权访问Nginx 静态资源鉴权访问Nginx 静态资源鉴权访问

SpringBoot配置

基本的DAO、实体不粘贴了,使用技术为Mybatis-plus

yml

server:
  port: 6001

# spring 配置
spring:
  application:
    name: auth-file
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: username
    password: password
    url: jdbc:mysql://mysql_address:port/auth-file?socketTimeout=30000&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true


# mybatis-plus 配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      update-strategy: ignored

鉴权Service

@Service
public class AuthService {

    @Resource
    UserRoleRepo userRoleRepo;
    @Resource
    FileRepo fileRepo;

    public Boolean auth(DownloadParams params) {
        QueryWrapper<UserRole> query = new QueryWrapper<>();
        query.eq("user_id", params.getUserId());
        List<Integer> currentUserRoles = userRoleRepo.selectList(query).stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());
        File file = fileRepo.selectById(params.getFileId());
        FileHolder.set(file.getFileName());
        if (currentUserRoles.contains(file.getRoleId())) {
            System.out.println("获得授权,开始下载");
            return true;
        } else {
            System.out.println("该用户没有文件: " + file.getFileName() + " 的下载权限");
            return false;
        }
    }
}

下载Service

此处为重点,值得注意的是,下载的response的Header内X-Accel-Redirect 就是回调Nginx静态前缀的关键所在

前面所提到的允许内部访问,即此处处理

@Service
public class DownloadService {
    @Resource
    FileRepo fileRepo;

    public void download(HttpServletResponse response, String fileName) {
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.setHeader("Content-Type", "application/octet-stream; charset=utf-8");
        response.setHeader("X-Accel-Redirect", "/resource/" + fileName);
        response.setHeader("X-Accel-Charset", "utf-8");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "No-cache");
        response.setHeader("Expires", "0");
    }

}

Web接口

@RestController
@RequestMapping("/file")
public class NgxAuthFileHandler {

    @Resource
    AuthService authService;
    @Resource
    DownloadService downloadService;

    @GetMapping
    public String preview(DownloadParams params, HttpServletResponse response) {
        if (authService.auth(params)) {
            String fileName = FileHolder.get();
            downloadService.download(response, fileName);
            return "获得授权,开始下载";
        } else {
            String fileName = FileHolder.get();
            return "该用户没有文件: " + fileName + " 的下载权限";
        }
    }


}
上一篇:tasksetCPU亲和力&docke容器资源限制stress测试


下一篇:使用nginx搭建音视频点播服务——基于DASH协议