Spring Boot 实现图片防盗链:Referer 校验与 Token 签名校验完整指南

Spring Boot 实现图片防盗链教程(Referer 校验 + Token 签名校验)

本文将详细讲解两种防盗链实现方案,并提供完整代码示例。


方案一:Referer 校验

通过检查 HTTP 请求头中的 Referer 字段判断来源是否合法。

实现步骤
  1. 创建 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;}
    }
  2. 注册拦截器到 Spring MVC

    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RefererInterceptor refererInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 拦截图片路径registry.addInterceptor(refererInterceptor).addPathPatterns("/images/**");}
    }
  3. 测试效果

  • 合法访问:<img src="https://yourdomain.com/images/cat.jpg">

  • 盗链访问:<img src="https://yourdomain.com/images/cat.jpg" 在其他网站使用时返回 403


方案二:Token 签名校验

通过动态生成的签名 token 验证请求合法性(更安全)。

实现原理
  1. 生成图片 URL 时添加参数:/images/cat.jpg?t=时间戳&sign=签名

  2. 服务器验证签名和时间戳有效性

实现步骤
  1. 生成签名工具类(用非对称加密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);}
    }
  2. 创建 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;}
    }
  3. 注册拦截器

    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Autowiredprivate TokenInterceptor tokenInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(tokenInterceptor).addPathPatterns("/images/**");}
    }
  4. 生成安全链接的 Controller

    @RestController
    public class ImageController {@GetMapping("/getImageUrl")public String getImageUrl(@RequestParam String imageName) {String imagePath = "/images/" + imageName;return TokenUtil.generateSignedUrl(imagePath);}
    }
  5. 前端使用示例

    <!-- 前端先请求获取合法链接 -->
    <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;
}
注意事项
  1. Referer 校验的局限性

    • 浏览器可能不发送 Referer(如HTTPS->HTTP)

    • 可通过 <meta name="referrer" content="no-referrer"> 绕过

  2. Token 校验最佳实践

    • 使用 HTTPS 防止 Token 被截获

    • 定期轮换密钥

    • 对 IP 进行访问频率限制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914506.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从 JSON 到 Python 对象:一次通透的序列化与反序列化之旅

目录 一、为什么要谈 JSON 二、最快速上手&#xff1a;两把钥匙 dumps 与 loads 三、深入 dumps&#xff1a;参数是魔法棒 四、深入 loads&#xff1a;把风险挡在门外 五、文件级序列化&#xff1a;dump 与 load 六、处理中文与编码陷阱 七、异常场景与调试技巧 八、实…

Leetcode 3315. 构造最小位运算数组 II

1.题目基本信息 1.1.题目描述 给你一个长度为 n 的质数数组 nums 。你的任务是返回一个长度为 n 的数组 ans &#xff0c;对于每个下标 i &#xff0c;以下 条件 均成立&#xff1a; ans[i] OR (ans[i] 1) nums[i] 除此以外&#xff0c;你需要 最小化 结果数组里每一个 a…

黑搜小知识 | DNS域名解析过程是什么样的?

什么是DNS&#xff1f;DNS( Domain Name System)是“域名系统”的英文缩写&#xff0c;是一种组织成域层次结构的计算机和网络服务命名系统&#xff0c;它用于TCP/IP网络&#xff0c;它所提供的服务是用来将主机名和域名转换为IP地址的工作。举例来说&#xff0c;如果你要访问域…

MyBatis 使用教程及插件开发

作者&#xff1a;小凯 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01; 本文的宗旨在于通过简单干净实践的方式教会读者&#xff0c;使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用&#xff0c;通过扩展插件…

Maui劝退:用windows直接真机调试iOS,无须和Mac配对

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

【极客日常】后端任务动态注入执行策略的一种技术实现

近期做项目时遇到一个场景&#xff0c;是需要在后端任务执行时动态注入策略。具体而言&#xff0c;笔者负责的后端服务&#xff0c;可以理解是会在线上服务发布时&#xff0c;对服务风险做实时扫描&#xff0c;那么这个扫描就需要根据当前线上服务发布上下文&#xff0c;匹配对…

8. JVM类装载的执行过程

1. JVM介绍和运行流程-CSDN博客 2. 什么是程序计数器-CSDN博客 3. java 堆和 JVM 内存结构-CSDN博客 4. 虚拟机栈-CSDN博客 5. JVM 的方法区-CSDN博客 6. JVM直接内存-CSDN博客 7. JVM类加载器与双亲委派模型-CSDN博客 8. JVM类装载的执行过程-CSDN博客 9. JVM垃圾回收…

Linux操作系统之信号:信号的产生

前言&#xff1a;上篇文章我们大致讲解了信号的有关概念&#xff0c;为大家引入了信号的知识点。但光知道那些是远远不够的。本篇文章&#xff0c;我将会为大家自己的讲解一下信号的产生的五种方式&#xff0c;希望对大家有所帮助。一、键盘&#xff08;硬件&#xff09;产生信…

pdf拆分

文章目录 背景目标实现下载 背景 好不容易下载的1000页行业报告&#xff0c;领导非要按章节拆分成20份&#xff01;学术论文合集需要按作者拆分投稿&#xff0c;手动分页到怀疑人生…客户发来加密合同&#xff0c;要求每5页生成独立文档&#xff0c;格式还不能乱&#xff01; …

vue3使用mermaid生成图表,并可编辑

效果图实际代码<template><div class"mermaid-container" style"z-index: 99999" ref"wrapperRef"><!-- 控制栏 --><div class"control-bar"><div class"control-bar-flex control-bar-tab-wrap"…

tcp/quic 的滑动窗口

一、滑动窗口 rwnd&#xff1a; 接收端窗口&#xff0c;接收方在每次发送ACK确认报文时&#xff0c;会包含一个 rwnd (Receive Window Size) 字段&#xff0c;指明自己当前剩余的接收缓冲区大小&#xff08;即可用窗口&#xff09;&#xff0c;这里是否是socket的接收缓冲区&am…

JVM监控及诊断工具-命令行篇

18.1. 概述 性能诊断是软件工程师在日常工作中需要经常面对和解决的问题&#xff0c;在用户体验至上的今天&#xff0c;解决好应用的性能问题能带来非常大的收益。 Java 作为最流行的编程语言之一&#xff0c;其应用性能诊断一直受到业界广泛关注。可能造成 Java 应用出现性能…

Jenkins 版本升级与插件问题深度复盘:从 2.443 到 2.504.3 及功能恢复全解析

前言&#xff1a;问题溯源与升级必要性 在 Jenkins 持续集成体系中&#xff0c;插件生态是其强大功能的核心驱动力。然而&#xff0c;某次例行维护中&#xff0c;团队对 Jenkins 2.443 环境的插件进行批量升级后&#xff0c;意外触发连锁反应 &#xff1a; SSH Server 插件功能…

Ribbon实战

一、前置知识 1.1 负载均衡定义 负载均衡指的是将网络请求通过不同的算法分配到不同的服务器上的技术&#xff0c;从而提升系统的性能。 1.2 负载均衡工具 负载均衡工具可以分分为客户端负载均衡工具和服务端负载均衡工具&#xff0c;它们的区别如下。 表1-1 负载均衡工具…

cs285学习笔记(一):课程总览

根据 Fall 2023 学期的官方课程日程&#xff0c;这里是 CS 285 全课程的 Lecture 大纲及内容摘要&#xff0c;详细对应周次和主题&#xff0c;方便你快速定位每节课要点、相关作业与视频资源 &#x1f3af; 官方课程地址 YouTobe 视频地址 blibli视频(带中文字幕) &#x…

OkHttp SSE 完整总结(最终版)

1. SSE 基础概念 什么是 SSE&#xff1f; SSE&#xff08;Server-Sent Events&#xff09;是一种 Web 标准&#xff0c;允许服务器向客户端推送实时数据。 核心特点 单向通信&#xff1a;服务器 → 客户端 基于 HTTP 协议&#xff1a;使用 GET 请求 长连接&#xff1a;连…

聚宽sql数据库传递

自建数据库从聚宽到Q-MT自动化交易实战 从接触聚宽以来一直都是手动跟单&#xff0c;在网上看到许多大佬的自动交易文章&#xff0c;心里也不禁十分痒痒。百说不如一练&#xff0c;千讲不如实干。经过一番努力&#xff0c;终于成功实盘了&#xff0c;效果还可以&#xff0c;几…

es里为什么node和shard不是一对一的关系

提问&#xff1a; 既然多个shard会被分配到同一个node上&#xff0c;那么为什么不把多个shard合并成一个然后存在当前node上呢&#xff0c;简而言之也就是让node和shard形成一对一的关系呢 &#xff1f;非常好的问题&#xff0c;这正是理解Elasticsearch分片&#xff08;shard…

浅谈npm,cnpm,pnpm,npx,nvm,yarn之间的区别

首先做一个基本的分类 名称描述npm,cnpm,yarn,pnpm都是Javascript包管理器nvm是Node.js版本控制器npx命令行工具 I.npm,cnpm,yarn,pnpm npm (Node Package Manager) npm是Node.js默认的包管理器&#xff0c;随Node.js的安装会一起安装。使用npm可以安装&#xff0c;发布&…

滑动窗口-76.最小覆盖子串-力扣(LeetCode)

一、题目解析1.不符合要求则返回空串("")2.子串中重复字符的数量要不少于t中该字符的数量二、算法原理解法1&#xff1a;暴力枚举哈希表这里的暴力枚举也可以优化&#xff0c;即在包含t中元素处枚举&#xff0c;如在A、B和C处开始枚举&#xff0c;减少不必要的枚举 解…