相信许多的小伙伴使用过Nginx服务器,来代理网站页面或者代理文件资源,配置简单,灵活。但是若出现像带权限的来访问Nginx的静态资源时,那简单的配置将不生效。
需要用到的知识、工具有
spingBoot
nginx
mysql
一些文件
我们来拿一个简单的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; # 指定提供文件鉴权服务的地址 } }
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;
四张表依次为 文件表、角色表、角色用户表、用户表
基本的DAO、实体不粘贴了,使用技术为Mybatis-plus
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 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; } } }
此处为重点,值得注意的是,下载的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"); } }
@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 + " 的下载权限"; } } }