前言
相信许多的小伙伴使用过Nginx服务器,来代理网站页面或者代理文件资源,配置简单,灵活。但是若出现像带权限的来访问Nginx的静态资源时,那简单的配置将不生效。
原理
准备
需要用到的知识、工具有
-
spingBoot
-
nginx
-
mysql
-
一些文件
开始
Nginx配置方式
我们来拿一个简单的Server的配置举例。
其中会出现几个重要的信息
-
/resource
下的internal
属性: 写上这个属性,即代表此前缀请求不对外开放,仅可以内部访问 -
/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-图
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;
文件准备
文件结构图
与之对应SQL表数据
四张表依次为 文件表、角色表、角色用户表、用户表
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 + " 的下载权限";
}
}
}