文件访问权限控制

1,558 阅读2分钟

前言

场景:对磁盘上的视频文件访问进行控制,因为视频可能包含敏感信息,所以不能让任何人都能随意访问。

思路

  • 访问视频

    视频文件存在专门磁盘目录下,如何让视频能被访问到?在SpringBoot中可以配置虚拟路径映射,使用ResourceHandlerRegistry 将本地磁盘路径映射成url来实现。

  • 访问控制

    使用Filter做访问控制,对上述映射出来的url进行拦截并校验访问权限。

实现

访问视频

  • 定义配置

    video-access:
      enable: true
      # url不以 / 结束
      url: /video
      # location以  / 结束
      location: G:/tmp/video-access/
      # ip 白名单
      whiteList:
        - 192.168.*.*
      # ip 黑名单
      blackList:
    
    @Data
    @ConfigurationProperties(prefix = "video-access")
    @Component
    public class VideoAccessConfig {
    
        // 是否开启访问校验
        private boolean enable = true;
    
        // 映射url
        private String url = "";
    
        // 视频文件路径
        private String location = "";
        
        // ip白名单
        private Set<String> whiteList = new HashSet<>(8);
    
        // ip黑名单
        private Set<String> blackList = new HashSet<>(8);
    
        public Set<String> getWhiteList() {
          // 本地默认添加到白名单
          whiteList.add("127.0.0.1");
          whiteList.add("0:0:0:0:0:0:0:1");
          return whiteList;
        }
    }
    
  • 添加映射

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Autowired
        private VideoAccessConfig videoAccessConfig;
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler(videoAccessConfig.getUrl() + "/**")
                    .addResourceLocations("file:" + videoAccessConfig.getLocation());
        }
    }
    

访问控制

  • 访问机制

    • ip是否在白名单中whiteList,在跳过
    • ip是否在黑名单中blackList,在拒绝
    • 自定义访问控制逻辑
  • 定义Filter

    @Component
    public class VideoAccessFilter extends OncePerRequestFilter {
    
        @Autowired
        private VideoAccessConfig videoAccessConfig;
        
        private AntPathMatcher antPathMatcher = new AntPathMatcher();
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
           // 此处获取ip简单处理;可通过请求头获取真实ip
            String ip = request.getRemoteAddr(); 
            // 白名单放行
            Set<String> whiteList = accessControlConfig.getWhiteList();
            boolean ignored = whiteList.stream().anyMatch(item -> antPathMatcher.match(item, ip));
            if (ignored) {
                filterChain.doFilter(request, response);
                return;
            }
    		// 黑名单拦截
            Set<String> blackList = accessControlConfig.getBlackList();
            boolean blocked = blackList.stream().anyMatch(item -> antPathMatcher.match(item, ip));
            if (blocked) {
                reject(response);
                return;
            }
            
            // TODO:此处自定义访问控制逻辑
            String token = httpServletRequest.getQueryString();
            if (videoAccessConfig.isEnable() && Objects.isNull(token)) {
                reject(response);
                return;
            }
            
            filterChain.doFilter(request, response);
        }
        private void reject(HttpServletResponse response) throws IOException {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getOutputStream().write("Access Denied".getBytes(StandardCharsets.UTF_8));
            response.getOutputStream().close();
        }
    }
    
  • 拦截url,使用FilterRegistrationBean注册Filter

    
    @Autowired
    private VideoAccessFilter videoAccessFilter;
    
    @Bean
    public FilterRegistrationBean filterOneRegister() {
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>();
            filterRegistrationBean.setFilter(videoAccessFilter);
            filterRegistrationBean.addUrlPatterns(videoAccessConfig.getUrl() + "/*");
            return filterRegistrationBean;
        }
    

测试

映射图片路径方便测试,测试urlqueryString表示有权限

  • 映射文件夹

文件夹.PNG

  • 无权限http://localhost:8080/video/miruiki.jpg

访问异常.PNG

  • 有权限http://localhost:8080/video/miruiki.jpg?token=1231

正常访问.PNG

结尾

如果文章对你有帮助,请点 赞👍🏻 支持一下。若有错误之处或有更好的建议,欢迎指正,谢谢。