Spring Boot 實現圖片防盜鏈教程(Referer 校驗 + Token 簽名校驗)
本文將詳細講解兩種防盜鏈實現方案,并提供完整代碼示例。
方案一:Referer 校驗
通過檢查 HTTP 請求頭中的?Referer
?字段判斷來源是否合法。
實現步驟
創建 Referer 攔截器
@Component public class RefererInterceptor implements HandlerInterceptor {private final List<String> allowedDomains = Arrays.asList("https://yourdomain.com", "https://trusted-site.com");@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 獲取 Referer 頭String referer = request.getHeader("Referer");// 允許直接訪問(如瀏覽器地址欄輸入)if (referer == null) return true;// 驗證 Referer 是否在白名單boolean isValid = allowedDomains.stream().anyMatch(domain -> referer.startsWith(domain));if (!isValid) {response.sendError(403, "Forbidden: Invalid Referer");return false;}return true;} }
注冊攔截器到 Spring MVC
@Configuration public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RefererInterceptor refererInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 攔截圖片路徑registry.addInterceptor(refererInterceptor).addPathPatterns("/images/**");} }
測試效果
合法訪問:
<img src="https://yourdomain.com/images/cat.jpg">
盜鏈訪問:
<img src="https://yourdomain.com/images/cat.jpg"
?在其他網站使用時返回?403
方案二:Token 簽名校驗
通過動態生成的簽名 token 驗證請求合法性(更安全)。
實現原理
生成圖片 URL 時添加參數:
/images/cat.jpg?t=時間戳&sign=簽名
服務器驗證簽名和時間戳有效性
實現步驟
生成簽名工具類(用非對稱加密RSA更安全)
public class TokenUtil {private static final String SECRET_KEY = "your_secret_123!";private static final long EXPIRE_SECONDS = 300; // 5分鐘有效期// 生成帶簽名的URLpublic static String generateSignedUrl(String imagePath) {long timestamp = System.currentTimeMillis() / 1000;String sign = generateSign(imagePath, timestamp);return imagePath + "?t=" + timestamp + "&sign=" + sign;}// 生成簽名 (使用HMAC-SHA256)private static String generateSign(String path, long timestamp) {String data = path + "|" + timestamp;return HmacUtils.hmacSha256Hex(SECRET_KEY, data);}// 驗證簽名public static boolean verifySign(String path, long timestamp, String sign) {// 檢查過期時間long current = System.currentTimeMillis() / 1000;if (current - timestamp > EXPIRE_SECONDS) return false;// 驗證簽名String expectedSign = generateSign(path, timestamp);return expectedSign.equals(sign);} }
創建 Token 校驗攔截器
@Component public class TokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String imagePath = request.getRequestURI();String sign = request.getParameter("sign");String timestampStr = request.getParameter("t");// 參數缺失檢查if (sign == null || timestampStr == null) {response.sendError(400, "Missing token parameters");return false;}try {long timestamp = Long.parseLong(timestampStr);if (!TokenUtil.verifySign(imagePath, timestamp, sign)) {response.sendError(403, "Invalid token");return false;}} catch (NumberFormatException e) {response.sendError(400, "Invalid timestamp format");return false;}return true;} }
注冊攔截器
@Configuration public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TokenInterceptor tokenInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor).addPathPatterns("/images/**");} }
生成安全鏈接的 Controller
@RestController public class ImageController {@GetMapping("/getImageUrl")public String getImageUrl(@RequestParam String imageName) {String imagePath = "/images/" + imageName;return TokenUtil.generateSignedUrl(imagePath);} }
前端使用示例
<!-- 前端先請求獲取合法鏈接 --> <script> fetch('/getImageUrl?imageName=cat.jpg').then(res => res.text()).then(url => {const img = document.createElement('img');img.src = url;document.body.appendChild(img);}); </script>
兩種方案對比
特性 | Referer 校驗 | Token 校驗 |
---|---|---|
安全性 | 中(Referer 可偽造) | 高(需破解簽名算法) |
實現復雜度 | 簡單 | 中等 |
鏈接有效期 | 永久 | 可控制時效 |
跨瀏覽器支持 | 部分瀏覽器禁用 Referer | 無兼容問題 |
防盜鏈效果 | 可防普通盜鏈 | 可防高級盜鏈 |
增強方案:雙驗證結合
// 在攔截器中組合驗證
public boolean preHandle(...) {// 先驗證 Refererif (!refererValid) {// 再驗證 Token(給合法合作方提供Token訪問方式)if (!tokenValid) {response.sendError(403);return false;}}return true;
}
注意事項
Referer 校驗的局限性:
瀏覽器可能不發送 Referer(如HTTPS->HTTP)
可通過?
<meta name="referrer" content="no-referrer">
?繞過
Token 校驗最佳實踐:
使用 HTTPS 防止 Token 被截獲
定期輪換密鑰
對 IP 進行訪問頻率限制