关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言
公司领导对数据安全非常看重,要求我们的系统中的图片链接,不能在其他地方打开,只能在系统中查看,要求我们整改系统。这玩意怎么整呀,图片资源服务器在我们公司本身就是可以对外公开访问图片的,怎么才能实现既要能访问又要不能访问呢?
防盗链是首先能想到的方案。我们今天一起来唠唠!
02 外部效果
我们先看看外部网站的效果,我们以百度图片为例:

通过查看源码,可以看到图片的具体地址:
但是直接访问时,却无法访问:

这就实现了图片的防盗功能。
03 防盗链的工作原理
防盗链的核心原理是检查HTTP请求头中的 Referer
字段。
Referer
字段表示这个请求是从哪个页面发起的。- 如果一张图片应该只在
www.mywebsite.com
上被访问,那么所有来自其他域名(如www.othersite.com
)的图片请求,其Referer
字段都会是http://www.othersite.com/somepage.html
。 - 服务器通过校验这个
Referer
值,就可以判断请求是否合法,从而决定是返回真实的图片还是一个错误/替代图片。
04 最佳实践
最高效、最常用的方式是在Web服务器层面配置,例如 Nginx
。这种方式性能损耗极小。此方案不是本节讨论的重点,我们从Java
开发角度取实现。
了解防盗链的工作原理之后,我们只需要通过拦截器或者过滤器,拦截指定的请求,通过Header
中的Referer
属性,就可以实现。
我们通过过滤器来实现,因为很多时候图片属于静态资源,而过滤去默认拦截静态资源。
4.1 创建拦截器
java
public class ImageReferFilter implements Filter {
public static final String REFERER = "Referer";
public static final String ALLOW_DOMAIN = "127.0.0.1:8080";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String url = httpServletRequest.getRequestURL().toString();
// 过滤图片资源
if (StringUtils.isNotEmpty(url) && StringUtils.endsWithAny(url.toLowerCase(), ".jpg", ".png", ".gif", ".jpeg")) {
String header = httpServletRequest.getHeader(REFERER);
// 来自本地,就放行
if (StringUtils.isNotEmpty(header) && StringUtils.contains(header, ALLOW_DOMAIN)) {
chain.doFilter(request, response);
return;
}
// 拦截输出403
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpResponse.getWriter().write("<h1>403 Forbidden</h1><p>You don't have permission to access the URL on this server.</p>");
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
输出结果可以根据自己的业务定制,如:
返回403禁止访问状态码
java
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden: Hotlinking is not allowed.");
请求重定向到指定图片
java
// 假设我们在网站根目录的 /images/ 下放了一张提示图片 anti-hotlinking.jpg
String warningImagePath = "/images/anti-hotlinking.jpg";
response.sendRedirect(warningImagePath);
4.2 配置拦截器
java
@Bean
FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filter = new FilterRegistrationBean();
filter.setFilter(new ImageReferFilter());
filter.addUrlPatterns("/*");
return filter;
}
4.3 页面展示
我们在static
下放了一张样例图,通过页面<img>
标签引用图片。

4.4 启动效果展示

我们也实现了类似百度图片防盗的效果。
05 注意事项
防盗链拦截器只能解决一般情况的图片防盗,并不能保证绝对的安全。所以使用时应该注意:
Referer
的可伪造性 :Referer
头可以被某些客户端或代理恶意修改或移除,因此防盗链机制并非绝对安全,但足以应对绝大多数普通的盗链情况。- 空
Referer
的处理 :需要谨慎处理Referer
为空的情况。浏览器直接输入地址、从HTTPS页面链接到HTTP资源(浏览器会屏蔽Referer
)等都会导致其为空。根据你的安全要求,可以选择允许或拒绝。上述Java示例选择了允许。 - CDN的影响 :如果你使用了CDN,需要确保CDN在回源请求时不会剥离或修改
Referer
头,或者将CDN的域名也加入白名单。 - 对用户体验的影响:确保你自己的网站不会被自己的规则所阻挡。在设置好之后,一定要全面测试自己网站的功能。