亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot實(shí)現(xiàn)圖片防盜鏈技術(shù)的原理分析與解決

 更新時(shí)間:2025年07月03日 10:06:13   作者:Micro麥可樂  
這篇文章主要為大家詳細(xì)介紹了SpringBoot中實(shí)現(xiàn)圖片防盜鏈技術(shù)的原理分析與完整解決方案,文中的示例代碼講解詳細(xì),需要的可以了解一下

1. 前言:圖片盜鏈的危害與影響

在現(xiàn)代 Web 應(yīng)用中,網(wǎng)站往往需要展示大量圖片資源(商品圖、文章配圖、用戶頭像等)。若不做防護(hù),其他站點(diǎn)或爬蟲可以直接引用這些圖片 URL,占用帶寬、盜用版權(quán)、造成服務(wù)器壓力過大會導(dǎo)致:

  • 流量損失:盜鏈消耗您的服務(wù)器帶寬
  • 成本增加:CDN和服務(wù)器費(fèi)用飆升
  • 版權(quán)侵犯:原創(chuàng)內(nèi)容被非法使用
  • SEO影響:搜索引擎排名下降

為此,我們需要為圖片資源加一道“防盜鏈”保護(hù),確保只有合法來源或攜帶正確憑證的請求才能成功獲取圖片。本文將帶著小伙伴們深入解析防盜鏈技術(shù)原理,并提供前后端完整解決方案。

2. 為什么要實(shí)施防盜鏈

我們先來看一個(gè)例子:假設(shè)你的云服務(wù)器按帶寬、流量計(jì)費(fèi),并且開通了CDN加速服務(wù),那么圖片被盜鏈你可能面臨下圖問題

所以實(shí)施防盜鏈,可以解決以下幾個(gè)問題:

1.節(jié)省帶寬與流量成本

非法盜鏈會導(dǎo)致大量免費(fèi)流量被外站消耗,增加服務(wù)器的網(wǎng)絡(luò)和流量費(fèi)用。

2.保護(hù)版權(quán)與資源安全

防止未授權(quán)站點(diǎn)隨意引用和傳播圖片資源,保障內(nèi)容提供方的利益。

3.防止爬蟲惡意抓取

結(jié)合簽名或 Referer 校驗(yàn),可以有效攔截簡單爬蟲,避免批量抓取。

4.提升訪問性能

當(dāng)檢測到非授權(quán)請求時(shí),直接返回 403 或空白圖,減輕后端壓力。

3. 防盜鏈核心技術(shù)原理

主要有兩種常見思路:

Referer 校驗(yàn):后端檢查 HTTP 請求頭中的 Referer,只有來自本站頁面的請求才允許訪問。

簽名(Token)校驗(yàn):前端在圖片 URL 上附加時(shí)間戳與簽名(HMAC/MD5),后端校驗(yàn)簽名并判斷是否過期。

Referer 校驗(yàn)簡單易用,但可以被偽造;簽名方案更安全,可以自定義過期時(shí)間、權(quán)限范圍。

3.1 Referer 校驗(yàn)

當(dāng)瀏覽器請求資源時(shí),會在Header中包含來源頁面地址:

GET /image.jpg HTTP/1.1
Host: your-domain.com
Referer: https://attacker-site.com/stolen-page.html

防盜鏈核心邏輯:

后端校驗(yàn)Referer:

@Configuration
public class SecurityConfig implements WebMvcConfigurer {
    //配置在yml文件中的合法域名
    @Value("${allowed.domains}")
    private List<String> allowedDomains;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RefererInterceptor(allowedDomains))
                .addPathPatterns("/images/**");
    }
}

public class RefererInterceptor implements HandlerInterceptor {
    
    private final Set<String> allowedDomains;
    
    public RefererInterceptor(List<String> domains) {
        this.allowedDomains = new HashSet<>(domains);
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        
        String referer = request.getHeader("Referer");
        if (referer == null) {
            // 允許無Referer的直接訪問(如瀏覽器地址欄)
            return true;
        }
        
        try {
            String domain = new URL(referer).getHost();
            if (allowedDomains.contains(domain)) {
                return true;
            }
        } catch (MalformedURLException e) {
            // URL格式錯(cuò)誤視為非法
        }
        
        // 返回防盜鏈提示圖
        response.setContentType("image/png");
        Files.copy(Paths.get("static/anti-leech.png"), 
                  response.getOutputStream());
        return false;
    }
}

3.2 簽名(Token)校驗(yàn)

3.2.1 前端實(shí)現(xiàn)思路

  • 計(jì)算簽名:用約定好的 secretKey 對圖片路徑(或文件名)+ 時(shí)間戳做 HMAC/MD5 計(jì)算。
  • 拼接 URL:/images/{filename}?ts={timestamp}&sign={signature}
  • 將帶簽名的 URL 輸出到頁面或組件內(nèi)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>防盜鏈?zhǔn)纠?lt;/title>
</head>
<body>
  <h3>商品展示圖:</h3>
  <img id="productImg" alt="Product">

  <script>
    // 約定的密鑰(不能泄露到公網(wǎng),示例僅展示邏輯)
    const SECRET_KEY = 'MySuperSecretKey';


     // 簡單 MD5 簽名(生產(chǎn)環(huán)境請使用 HMAC)
     // 這里只借助外部庫 md5.min.js
    function generateSignedUrl(filename) {
      const ts = Date.now();
      // 簽名內(nèi)容:filename + ts + SECRET_KEY
      const raw = `${filename}${ts}${SECRET_KEY}`;
      const sign = md5(raw);
      return `/images/${filename}?ts=${ts}&sign=${sign}`;
    }

    // 使用示例
    document.getElementById('productImg').src = generateSignedUrl('sample.jpg');
  </script>
  <!-- 引入 md5 庫 -->
  <script src="https://cdn.jsdelivr.net/npm/blueimp-md5/js/md5.min.js"></script>
</body>
</html>

3.2.1 后端(Spring Boot)示例

添加依賴

Spring Boot 項(xiàng)目中追加 commons-codec 依賴

<!-- pom.xml -->
<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.15</version>
</dependency>

編寫簽名校驗(yàn)攔截器

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

@Component
public class ImageAuthInterceptor implements HandlerInterceptor {

    private static final String SECRET_KEY = "MySuperSecretKey";
    // 簽名有效期:5 分鐘
    private static final long EXPIRE_MILLIS = 5 * 60 * 1000;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String tsParam = request.getParameter("ts");
        String signParam = request.getParameter("sign");
        String uri = request.getRequestURI(); // e.g. /images/sample.jpg

        if (tsParam == null || signParam == null) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return false;
        }

        long ts = Long.parseLong(tsParam);
        long now = System.currentTimeMillis();
        if (now - ts > EXPIRE_MILLIS) {  // 超時(shí)
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "鏈接過期");
            return false;
        }

        // 計(jì)算服務(wù)器端簽名
        String path = uri.substring("/images/".length()); // sample.jpg
        String raw = path + tsParam + SECRET_KEY;
        String serverSign = DigestUtils.md5Hex(raw);

        if (!serverSign.equalsIgnoreCase(signParam)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return false;
        }

        // 簽名校驗(yàn)通過,繼續(xù)處理請求(交給靜態(tài)文件 handler)
        return true;
    }
}

注冊攔截器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private ImageAuthInterceptor imageAuthInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(imageAuthInterceptor)
                .addPathPatterns("/images/**");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 將 /images/** 映射到本地文件系統(tǒng)目錄
        registry.addResourceHandler("/images/**")
                .addResourceLocations("file:/opt/app/images/");
    }
}

存放圖片

將需要防盜鏈的圖片放到服務(wù)器 /opt/app/images/ 目錄下,例如 sample.jpg

4. 高級防護(hù)策略

通過上述的代碼演示,無論你是使用 Referer 校驗(yàn),還是基于簽名校驗(yàn),相信小伙伴已經(jīng)可以輕松應(yīng)用于自己的項(xiàng)目中,這里博主再簡單羅列兩點(diǎn)高級防護(hù)策略,供小伙伴們參考

4.1 動(dòng)態(tài)水印技術(shù)

將圖片資源默認(rèn)都加上動(dòng)態(tài)水印

public void addWatermark(InputStream imageStream, 
                         OutputStream output, 
                         String text) throws IOException {
    
    BufferedImage image = ImageIO.read(imageStream);
    Graphics2D g = image.createGraphics();
    
    // 設(shè)置水印透明度
    g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
    g.setColor(Color.BLACK);
    g.setFont(new Font("Arial", Font.BOLD, 30));
    
    // 計(jì)算水印位置
    FontMetrics metrics = g.getFontMetrics();
    int x = (image.getWidth() - metrics.stringWidth(text)) / 2;
    int y = image.getHeight() - 50;
    
    // 添加文字水印
    g.drawString(text, x, y);
    g.dispose();
    
    ImageIO.write(image, "jpg", output);
}

4.2 智能行為分析

我們還可以設(shè)置一些行為限制,比如 幾秒內(nèi)可以訪問多少次

@Component
public class ImageRequestAnalyzer {
    
    private final Map<String, RequestCounter> ipCounters = new ConcurrentHashMap<>();
    
    @Scheduled(fixedRate = 60000) // 每分鐘清理
    public void cleanCounters() {
        ipCounters.entrySet().removeIf(entry -> 
            entry.getValue().isExpired());
    }
    
    public boolean isSuspiciousRequest(HttpServletRequest request) {
        String ip = request.getRemoteAddr();
        String path = request.getRequestURI();
        
        RequestCounter counter = ipCounters.computeIfAbsent(
            ip + path, k -> new RequestCounter());
        
        counter.increment();
        
        // 規(guī)則1: 10秒內(nèi)超過20次請求
        if (counter.getCount(10) > 20) return true;
        
        // 規(guī)則2: 1分鐘內(nèi)超過100次請求
        if (counter.getCount(60) > 100) return true;
        
        // 規(guī)則3: 異常User-Agent
        String ua = request.getHeader("User-Agent");
        if (ua == null || ua.contains("Python") || ua.contains("curl")) {
            return true;
        }
        
        return false;
    }
    
    static class RequestCounter {
        private final List<Long> timestamps = new ArrayList<>();
        
        public synchronized void increment() {
            timestamps.add(System.currentTimeMillis());
        }
        
        public synchronized int getCount(int seconds) {
            long cutoff = System.currentTimeMillis() - seconds * 1000L;
            timestamps.removeIf(t -> t < cutoff);
            return timestamps.size();
        }
        
        public boolean isExpired() {
            return timestamps.isEmpty() || 
                System.currentTimeMillis() - timestamps.get(0) > 3600000;
        }
    }
}

5. 結(jié)語

本文講解了 Referer校驗(yàn) + 簽名校驗(yàn)兩種防盜鏈方案,其中簽名校驗(yàn) 我們實(shí)現(xiàn)在前端生成帶有防盜鏈簽名的圖片 URL,后端(Spring Boot)在攔截器中校驗(yàn)簽名并檢查有效期,只有合法請求才能獲取圖片資源。該方案優(yōu)點(diǎn)在于:

  • 安全性高:簽名不可偽造,且可設(shè)置過期
  • 靈活可擴(kuò)展:可加入用戶鑒權(quán)、權(quán)限控制等
  • 輕量無侵入:僅依賴攔截器和靜態(tài)資源映射

以上就是SpringBoot實(shí)現(xiàn)圖片防盜鏈技術(shù)的原理分析與解決的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot圖片防盜鏈的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法

    IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法

    這篇文章主要介紹了IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法,文中有非常詳細(xì)的圖文示例,對出現(xiàn)Server returns invalid timezone. Go to ‘Advanced‘ tab and set ‘serverTimezone‘ prope報(bào)錯(cuò)的小伙伴們很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • Spring事務(wù)的七種傳播行為

    Spring事務(wù)的七種傳播行為

    這篇文章主要介紹了Spring事務(wù)的七種傳播行為,文章圍繞主題的相關(guān)資料展開詳細(xì)的內(nèi)容解說,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-04-04
  • Mybatis查詢時(shí)的延遲加載解析

    Mybatis查詢時(shí)的延遲加載解析

    這篇文章主要介紹了Mybatis查詢時(shí)的延遲加載解析,先從單表查詢,需要時(shí)再從關(guān)聯(lián)表去關(guān)聯(lián)查詢,能大大提高數(shù)據(jù)庫性能,因?yàn)椴樵儐伪硪汝P(guān)聯(lián)查詢多張表速度要快,延遲加載分為兩種:深度延時(shí)加載,侵入式延遲加載,需要的朋友可以參考下
    2023-10-10
  • Springmvc國際化自動(dòng)配置代碼實(shí)現(xiàn)

    Springmvc國際化自動(dòng)配置代碼實(shí)現(xiàn)

    這篇文章主要介紹了Springmvc國際化自動(dòng)配置代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • spring依賴注入原理與用法實(shí)例分析

    spring依賴注入原理與用法實(shí)例分析

    這篇文章主要介紹了spring依賴注入原理與用法,結(jié)合實(shí)例形式分析了spring框架依賴注入的概念、原理、用法案例及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-10-10
  • springboot中application.yml多環(huán)境生效規(guī)則說明

    springboot中application.yml多環(huán)境生效規(guī)則說明

    這篇文章主要介紹了springboot中application.yml多環(huán)境生效規(guī)則說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • java處理日期的工具類DateUtil

    java處理日期的工具類DateUtil

    這篇文章主要為大家詳細(xì)介紹了java處理日期的工具類DateUtil,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • 保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性詳解

    保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性詳解

    在實(shí)際開發(fā)過程中,緩存的使用頻率是非常高的,只要使用緩存和數(shù)據(jù)庫存儲,就難免會出現(xiàn)雙寫時(shí)數(shù)據(jù)一致性的問題,本文主要介紹了如何保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性,需要的小伙伴可以參考閱讀
    2023-04-04
  • Java的NIO與IO的詳解及對比

    Java的NIO與IO的詳解及對比

    這篇文章主要介紹了Java的NIO與IO的詳解及對比的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Java接入支付寶授權(quán)第三方登錄的完整步驟

    Java接入支付寶授權(quán)第三方登錄的完整步驟

    不管是支付寶支付,還是微信支付,還是銀聯(lián)支付等,大部分的支付流程都是相似的,這篇文章主要給大家介紹了關(guān)于Java接入支付寶授權(quán)第三方登錄的相關(guān)資料,使用支付寶的沙盒環(huán)境示例,需要的朋友可以參考下
    2021-07-07

最新評論