前言
相信許多的小伙伴使用過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 + " 的下載權限";
}
}
}