Implementing Image Anti-Hotlinking in Spring Boot with a Configurable Interceptor
This article demonstrates how to implement image anti-hotlinking in a Spring Boot application by creating a configurable interceptor, registering it, and providing flexible YAML settings, while discussing limitations such as referer spoofing and potential false positives.
Introduction
For security reasons we may want backend‑served images to be displayed only on a specific website, preventing crawlers from downloading them, and for performance we want to avoid other sites hotlinking our images and consuming server resources.
1. Simple Implementation
1‑1 Create Interceptor Class
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ImageProtectionInterceptor implements HandlerInterceptor {
private static final String ALLOWED_DOMAIN = "baidudu.com"; // allowed domain
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Get request URL
String requestUrl = request.getRequestURL().toString();
// Check if request ends with image suffix
if (requestUrl.endsWith(".jpg") || requestUrl.endsWith(".png") || requestUrl.endsWith(".jpeg")) {
// Get Referer header
String referer = request.getHeader("Referer");
// Allow if referer contains allowed domain
if (referer != null && referer.contains(ALLOWED_DOMAIN)) {
return true; // allow
} else {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false; // block
}
}
return true; // allow non‑image resources
}
}1‑2 Register Interceptor
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Register interceptor for all paths
registry.addInterceptor(new ImageProtectionInterceptor())
.addPathPatterns("/**"); // intercept all requests
}
}2. Flexible Configuration
2‑1 application.yml
# Image anti‑hotlinking configuration
img-protect:
enabled: true
allowBrowser: false
allowReferer: baidudu.com2‑2 Configuration Properties Class
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("img-protect")
public class ImgProtectConfig {
private boolean enabled;
private boolean allowBrowser;
private String allowReferer;
public boolean getEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean getAllowBrowser() { return allowBrowser; }
public void setAllowBrowser(boolean allowBrowser) { this.allowBrowser = allowBrowser; }
public String getAllowReferer() { return allowReferer; }
public void setAllowReferer(String allowReferer) { this.allowReferer = allowReferer; }
}2‑3 Flexible Interceptor
import 上方2-2创建的类路径.ImgProtectConfig;
import org.springframework.beans.factory.annotation.Autowired;
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.HashSet;
import java.util.Set;
@Component
public class ImageProtectionInterceptor implements HandlerInterceptor {
@Autowired
private ImgProtectConfig imgProtectConfig;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Check if feature is enabled
if (!imgProtectConfig.getEnabled()) {
return true;
}
String requestUrl = request.getRequestURL().toString();
if (requestUrl.endsWith(".jpg") || requestUrl.endsWith(".png") || requestUrl.endsWith(".jpeg")) {
String referer = request.getHeader("Referer");
if (referer == null && imgProtectConfig.getAllowBrowser()) {
return true; // allow direct browser access
} else if (referer != null && isAllowedDomain(referer)) {
return true; // allowed referer
} else {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}
return true;
}
// Check if referer is in whitelist
private boolean isAllowedDomain(String referer) {
String allowedReferers = imgProtectConfig.getAllowReferer();
if (allowedReferers != null && !"".equals(allowedReferers.trim())) {
Set
allowedDomains = new HashSet<>(Arrays.asList(allowedReferers.split(",")));
for (String allowedDomain : allowedDomains) {
if (referer.contains(allowedDomain.trim())) {
return true;
}
}
}
return false;
}
}2‑4 Register Flexible Interceptor
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private ImageProtectionInterceptor imageProtectionInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Register interceptor for all paths
registry.addInterceptor(imageProtectionInterceptor)
.addPathPatterns("/**"); // intercept all requests
}
}Conclusion
The above anti‑hotlinking interceptors can handle typical image hotlinking scenarios, but they cannot guarantee absolute security; issues such as referer spoofing, false positives/negatives, and proxy tricks may still arise.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.