本文目录导读:

我来为您提供一个完整的Java防盗链实现案例,防盗链主要用于防止其他网站直接引用您的资源(如图片、视频、文件等)。
基于Spring Boot的防盗链实现
使用拦截器(Interceptor)实现
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
/**
* 防盗链拦截器
*/
@Component
public class AntiHotlinkInterceptor implements HandlerInterceptor {
// 允许访问的域名白名单
private static final List<String> ALLOWED_DOMAINS = Arrays.asList(
"www.example.com",
"example.com",
"localhost",
"127.0.0.1"
);
// 需要防盗链保护的路径
private static final List<String> PROTECTED_PATHS = Arrays.asList(
"/images/",
"/files/",
"/video/",
"/static/resources/"
);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestURI = request.getRequestURI();
// 检查是否需要防盗链保护
if (!isProtectedPath(requestURI)) {
return true; // 不需要保护,直接放行
}
// 获取Referer头信息
String referer = request.getHeader("Referer");
// 直接访问(无Referer)或Referer为空
if (referer == null || referer.isEmpty()) {
// 可以选择允许直接访问或拒绝
// 这里设为拒绝直接访问
handleBlockedRequest(response);
return false;
}
// 检查Referer是否在白名单中
if (!isAllowedReferer(referer)) {
handleBlockedRequest(response);
return false;
}
return true; // 验证通过,放行
}
/**
* 判断是否是需要保护的路径
*/
private boolean isProtectedPath(String path) {
return PROTECTED_PATHS.stream().anyMatch(path::contains);
}
/**
* 判断Referer是否合法
*/
private boolean isAllowedReferer(String referer) {
try {
java.net.URL url = new java.net.URL(referer);
String domain = url.getHost().toLowerCase();
return ALLOWED_DOMAINS.stream()
.anyMatch(domain::equalsIgnoreCase);
} catch (Exception e) {
return false; // URL解析失败视为非法请求
}
}
/**
* 处理被阻止的请求
*/
private void handleBlockedRequest(HttpServletResponse response)
throws Exception {
response.setContentType("text/html;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
// 可以返回一个提示图片或者错误信息
response.getWriter().write("""
<html>
<body style="text-align:center;padding:50px;">
<h1 style="color:red;">403 - 禁止盗链</h1>
<p>您的请求来源不合法,请通过正规渠道访问。</p>
<img src="/images/anti-hotlink.png" alt="禁止盗链"/>
</body>
</html>
""");
}
}
使用过滤器(Filter)实现
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 防盗链过滤器
*/
@WebFilter(urlPatterns = "/*", filterName = "antiHotlinkFilter")
public class AntiHotlinkFilter implements Filter {
// 使用Set存储允许的域名,并且支持动态更新
private Set<String> allowedDomains = ConcurrentHashMap.newKeySet();
// 需要保护的资源类型
private Set<String> protectedExtensions = ConcurrentHashMap.newKeySet();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化允许的域名
allowedDomains.add("localhost");
allowedDomains.add("127.0.0.1");
allowedDomains.add("www.example.com");
allowedDomains.add("example.com");
// 初始化需要保护的资源类型
protectedExtensions.add(".jpg");
protectedExtensions.add(".jpeg");
protectedExtensions.add(".png");
protectedExtensions.add(".gif");
protectedExtensions.add(".bmp");
protectedExtensions.add(".ico");
protectedExtensions.add(".mp4");
protectedExtensions.add(".avi");
protectedExtensions.add(".pdf");
protectedExtensions.add(".mp3");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
// 检查是否为需要保护的资源
if (!isProtectedResource(requestURI)) {
chain.doFilter(request, response); // 非保护资源直接放行
return;
}
// 检查请求来源
String referer = httpRequest.getHeader("Referer");
// 允许同域名站内的请求没有Referer(直接访问)或来自允许的域名
if (isValidReferer(referer)) {
chain.doFilter(request, response);
} else {
// 非法请求,返回错误信息
respondWithError(httpResponse);
}
}
/**
* 判断是否是受保护的资源
*/
private boolean isProtectedResource(String uri) {
// 检查文件扩展名
int lastDotIndex = uri.lastIndexOf(".");
if (lastDotIndex == -1 || lastDotIndex == uri.length() - 1) {
return false;
}
String extension = uri.substring(lastDotIndex).toLowerCase();
return protectedExtensions.contains(extension);
}
/**
* 验证Referer是否合法
*/
private boolean isValidReferer(String referer) {
// 无Referer的请求(直接访问)默认允许
if (referer == null || referer.isEmpty()) {
return true;
}
try {
URL url = new URL(referer);
String host = url.getHost().toLowerCase();
return allowedDomains.contains(host);
} catch (Exception e) {
return false;
}
}
/**
* 返回错误响应
*/
private void respondWithError(HttpServletResponse response) throws IOException {
// 方式1:返回自定义图片
// response.getOutputStream().write(getBlockImageBytes());
// 方式2:返回错误状态和信息
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("{\"code\":403,\"message\":\"请求被拒绝:检测到盗链行为\"}");
}
@Override
public void destroy() {
// 清空资源
allowedDomains.clear();
protectedExtensions.clear();
}
}
Web MVC配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web MVC配置
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册防盗链拦截器
registry.addInterceptor(new AntiHotlinkInterceptor())
// 指定拦截的路径
.addPathPatterns("/images/**", "/files/**", "/video/**")
// 排除不需要拦截的路径
.excludePathPatterns("/images/logo.png", "/favicon.ico");
}
}
前端HTML测试页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">防盗链测试页面</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.test-card {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
img, video {
max-width: 100%;
height: auto;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
}
button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<h1 style="text-align:center;">防盗链功能测试页面</h1>
<div class="test-card">
<h2>正常访问(本站)</h2>
<p>此图片来自本站服务器,应该可以正常显示:</p>
<img src="/images/sample.jpg" alt="本地图片">
</div>
<div class="test-card">
<h2>测试视频资源</h2>
<p>此视频来自本站服务器:</p>
<video controls>
<source src="/video/sample.mp4" type="video/mp4">
您的浏览器不支持视频标签
</video>
</div>
<div class="test-card">
<h2>模拟外部引用</h2>
<p>以下按钮模拟外部网站请求本站资源(实际测试中会携带外部Referer):</p>
<button onclick="testExternalLink()">模拟外部链接点击</button>
<button onclick="testDirectAccess()">直接访问测试</button>
<div id="result"></div>
</div>
<script>
// 模拟外部链接引用
function testExternalLink() {
// 这里在实际场景中,请求会带有外部网站的Referer
var imgUrl = '/images/protected-image.png';
fetch(imgUrl, {
headers: {
'Referer': 'https://evil-site.com/page.html'
}
})
.then(response => {
if (response.status === 403) {
document.getElementById('result').innerHTML =
'<p style="color:red;">❌ 请求被拒�绝:检测到盗链行为(403)</p>';
} else {
document.getElementById('result').innerHTML =
'<p style="color:green;">✅ 请求成功</p>';
}
})
.catch(error => {
document.getElementById('result').innerHTML =
'<p style="color:red;">❌ 请求出错: ' + error.message + '</p>';
});
}
// 直接访问测试
function testDirectAccess() {
var img = new Image();
img.src = '/images/protected-image.png';
img.onload = function() {
document.getElementById('result').innerHTML =
'<p style="color:green;">✅ 直接访问成功(图片已加载)</p>';
};
img.onerror = function() {
document.getElementById('result').innerHTML =
'<p style="color:red;">❌ 直接访问被拒绝</p>';
};
}
</script>
</body>
</html>
高级防盗链实现(基于令牌)
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基于令牌的防盗链工具类
*/
@Component
public class TokenBasedAntiHotlink {
// 存储有效的令牌(实际场景可能使用Redis等分布式存储)
private ConcurrentHashMap<String, Long> tokenStore = new ConcurrentHashMap<>();
// 令牌有效期(毫秒)
private static final long TOKEN_VALIDITY = 24 * 60 * 60 * 1000; // 24小时
// 加密密钥
private static final String SECRET_KEY = "your-secret-key-here";
/**
* 生成访问令牌
*/
public String generateToken(String resourcePath) {
long timestamp = System.currentTimeMillis();
String rawToken = resourcePath + timestamp + SECRET_KEY;
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(rawToken.getBytes());
String token = Base64.getUrlEncoder().withoutPadding()
.encodeToString(hash);
// 存储令牌和时间戳
tokenStore.put(token, timestamp);
return token + "." + timestamp;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("加密算法不可用");
}
}
/**
* 验证令牌
*/
public boolean validateToken(String token, String resourcePath) {
try {
// 解析令牌和时间戳
String[] parts = token.split("\\.");
if (parts.length != 2) {
return false;
}
String tokenPart = parts[0];
long timestamp = Long.parseLong(parts[1]);
// 检查是否过期
if (System.currentTimeMillis() - timestamp > TOKEN_VALIDITY) {
tokenStore.remove(tokenPart);
return false;
}
// 验证令牌是否存在
Long storedTimestamp = tokenStore.get(tokenPart);
if (storedTimestamp == null || storedTimestamp != timestamp) {
return false;
}
// 验证令牌与资源路径的关联
String expectedToken = generateHash(resourcePath + timestamp + SECRET_KEY);
if (!expectedToken.equals(tokenPart)) {
return false;
}
// 验证成功,移除令牌(一次性使用)
tokenStore.remove(tokenPart);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 生成哈希值
*/
private String generateHash(String input) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(input.getBytes());
return Base64.getUrlEncoder().withoutPadding().encodeToString(hash);
}
}
配置文件示例(application.yml)
# 应用程序配置
spring:
application:
name: anti-hotlink-demo
mvc:
static-path-pattern: /static/**
# 自定义防盗链配置
anti-hotlink:
# 是否启用防盗链
enabled: true
# 允许的域名列表(使用逗号分隔)
allowed-domains: >
www.example.com,
example.com,
localhost,
*.example.com
# 需要保护的路径模式
protected-paths:
- /images/**
- /files/**
- /video/**
- /static/**
# 排除的路径
excluded-paths:
- /images/logo.png
- /favicon.ico
# 拒绝行为
block-redirect-url: /error/403.html
block-message: 禁止外部链接访问本站资源
使用说明
主要特点:
- 多层防护:支持拦截器、过滤器、令牌多种方式
- 灵活配置:可以通过配置文件或代码设置白名单
- 多种保护:支持图片、视频、文档等多种资源类型
- 错误处理:自动识别盗链行为并返回相应错误
部署要点:
- 确保服务器时区正确
- 合理设置白名单域名
- 对于分布式环境,建议使用Redis存储令牌
- 定期更新密钥和安全性配置
这个案例提供了完整的防盗链解决方案,可以根据实际需求选择合适的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播