• 防止非法盗链的几种解决方案


    非法盗链概述

    非法盗链指的是在未获得授权的情况下,将别人的资源(如图片、视频等)直接链接到自己的网站上,从而消耗他人的带宽和流量,并影响原始资源的安全性

    HTTP Referer请求头验证

    通过检查HTTP头部中的Referer字段,判断请求来源是否合法。只允许来自特定域名或IP地址的请求才能访问资源。这种方法较容易实施,不过存在一定的伪造风险。

    Spring MVC项目

    创建RefererFilter类实现Filter接口并重写3个方法,实现过滤逻辑。

    在处理请求时判断请求头中的Referer字段,然后通过字符串比较或正则匹配可以判断请求来源是否合法,如果不合法可以返回错误信息或重定向到其他页面。

    public class RefererFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    		System.out.println("初始化方法...");
        }
    
        /**
         * @param request
         * @param response
         * @param chain
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     	    System.out.println("拦截请求...");
     	    
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            //每次请求来源
            String referer = req.getHeader("referer");
            //获取请求地址
            String serverName = req.getServerName();
            
            System.out.println("referer:" + referer + "---serverName" + serverName);
            
            if (referer == null || !referer.contains(serverName)) {
                req.getRequestDispatcher("/static/images/error.png").forward(req, res);
                return;
            }
            //放行
            chain.doFilter(req, res);
        }
    
    
        @Override
        public void destroy() {
    		System.out.println("销毁请求...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    web.xml配置过滤器

    	<filter>
    		<filter-name>RefererFilter</filter-name>
    		<filter-class>cn.ybzy.demo.fileter.RefererFilter</filter-class>
    	</filter>
    	
    	<filter-mapping>
    		<filter-name>RefererFilter</filter-name>
    		<url-pattern>/static/images/*
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Spring Boot项目

    创建一个拦截器来对 HTTP Referer 请求头进行验证

    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class RefererInterceptor implements HandlerInterceptor {
        // 允许的 referer
        private static final String ALLOWED_REFERER = "https://www.your-domain.com";
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String referer = request.getHeader("Referer");
            // 如果请求头中的 Referer 不是允许的域名,则返回 403 错误
            if (referer == null || !referer.startsWith(ALLOWED_REFERER)) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
                return false;
            }
            // 否则放行
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    通过 WebMvcConfigurer 接口和 addInterceptors 方法注册了名为 RefererInterceptor 的拦截器。

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new RefererInterceptor());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用Nginx

    可以使用Nginx通过配置实现非法盗链防护,主要使用valid_referers 参数

    valid_referers:指定允许访问该网站资源的有效来源,可以设置为多个值,用空格分隔,如valid_referers example.com *.example.com; 表示只允许来自example.com或者其子域名的访问。

    如果请求的 Referer 头不存在或者不在 valid_referers 中,则会将 $invalid_referer 设置为 1,进而返回 HTTP 状态码 403

    具体实现如下:

    location ~ .*\.(jpg|jpeg|JPG|png|gif|icon)$ {
            valid_referers blocked example.com *.example.com;
    		if ($invalid_referer) {
    		    return 403;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    限制访问权限

    在资源服务器和客户端上分别进行配置,实现认证和授权机制。服务端可以使用JavaEE 中的Servlet、Filter 或Spring Security等框架来实现认证和授权。客户端则需要在请求中携带身份认证凭证,如token、session id等

    客户端发送请求时携带身份认证凭证:

      # 用户身份认证凭证
      String token = "123456789";
      HttpURLConnection conn = (HttpURLConnection) new URL("http://www.test.com/img.jpg").openConnection();
      // 在请求头中添加 Authorization 字段携带凭证
      conn.setRequestProperty("Authorization", "Bearer "+token); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    服务端进行认证:

      // 获取请求头中的认证字段
      String authHeader = request.getHeader("Authorization"); 
      
      if(authHeader != null && authHeader.startsWith("Bearer ")){
        // 提取认证凭证
        String token = authHeader.substring(7); 
        // TODO 对凭证进行校验或者查询授权信息
      } else{
        
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    动态生成链接

    在服务端对每个请求生成唯一的一次性链接,在处理请求时验证该链接是否合法。可以使用Java中的UUID类来生成随机字符串作为链接的参数。这种方法能够有效保护资源的安全,但对服务器压力较大。

    生成唯一带签名的链接

        private static final String KEY = "YOUR_SECRET_KEY"; // 设置秘钥
    
    	/**
         * 传入原始链接作为参数,即可生成带有时间戳、随机数和签名的新链接
         * @param url 访问URL
         */
        public static String generateLink(String url) throws NoSuchAlgorithmException {
            // 当前时间戳
            String timestamp = System.currentTimeMillis() + "";
            // 随机数
            String nonce = Long.toString(Math.round(Math.random() * 10000));
            // 签名内容
            String rawSignature = url + timestamp + nonce + KEY;
            // 使用 SHA-256 算法生成签名
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] signature = md.digest(rawSignature.getBytes());
            // Base64 编码
            String encodedSignature = Base64.getEncoder().encodeToString(signature);
            // 生成链接
            String link = url + "?timestamp=" + timestamp + "&nonce=" + nonce + "&signature=" + encodedSignature;
            return link;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    校验链接是否合法

    校验步骤:

    从链接中取出 timestamp、nonce 和 signature 三个参数
    
    使用与链接生成时相同的秘钥、算法(SHA-256)和编码方式(Base64)对原始内容进行签名,获得新的签名值
    
    比较从链接中获得的签名值和新的签名值是否一致
    
    如果签名值一致,并且 timestamp 和 nonce 参数符合要求(如没有重复),则认为该链接是合法的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    	private static final String KEY = "YOUR_SECRET_KEY"; // 设置秘钥
    
        /**
         * 如果返回 true 则说明链接合法,可以继续访问
         * 如果返回 false,则说明链接不合法,需要返回错误信息或者重定向到其他页面
         * @param url 访问URL
         * @param timestamp 时间戳
         * @param nonce 随机数
         * @param signature 签名
         */
        public static boolean isValidLink(String url, String timestamp, String nonce, String signature) throws NoSuchAlgorithmException {
            // 签名内容
            String rawSignature = url + timestamp + nonce + KEY;
            // 使用 SHA-256 算法生成签名
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] newSignature = md.digest(rawSignature.getBytes());
            // Base64 编码
            String encodedSignature = Base64.getEncoder().encodeToString(newSignature);
    
            // 校验签名值和其他参数
            return encodedSignature.equals(signature);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试

        public static void main(String[] args) throws Exception {
            String link = DynamicLink.generateLink("/test");
            // link = /test?timestamp=1683100020877&nonce=9081&signature=vD2S/Ki7a/SLhRj19mcqDmylKKO3OTKTtpA/CUZJMg0=
            System.out.println("link = " + link);
    
            boolean validLink = DynamicLink.isValidLink("/test", "1683100020877", "9081", "vD2S/Ki7a/SLhRj19mcqDmylKKO3OTKTtpA/CUZJMg0=");
            System.out.println("validLink = " + validLink);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    link = /test?timestamp=1683100020877&nonce=9081&signature=vD2S/Ki7a/SLhRj19mcqDmylKKO3OTKTtpA/CUZJMg0=
    validLink = true
    
    • 1
    • 2

    添加水印

    在资源服务器上进行配置,在服务端对资源进行加水印处理,并在客户端显示处理后的结果。Java可以使用Graphics2D工具类来添加水印。

    参考:Java实现添加文字水印、图片水印功能

    CDN防盗链

    利用CDN服务商的防盗链功能,限制只有经过授权的域名才能访问资源。

    访问频率限制

    通过IP地址或用户会话ID等方式限制同一客户端在一段时间内对资源的访问频率,防止非法爬取和盗链。

  • 相关阅读:
    GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图
    如何修改默认YUM仓库 安装扩展仓库
    适用于嵌入式arm的ffmpeg编解码
    HTML+CSS简单漫画网页设计成品--(红猪(9页)带注释)
    基于Matlab求解高教社杯全国大学生数学建模竞赛(CUMCM2004A题)-奥运会临时超市网点设计(附上源码+数据)
    测试开发3——用例篇
    Camera/Lens流程(2)——异常分析
    【紫光同创国产FPGA教程】——【PGL22G第八章】HDMI输出彩条实验例程
    以人为本是AI大模型的最终落脚点——读《大模型时代:ChatGPT开启通用人工智能浪潮》
    hive电子商务消费行为分析
  • 原文地址:https://blog.csdn.net/qq_38628046/article/details/133436335