防盗链是保护资源服务器的常用方法,旨在防止未经授权的外部链接直接访问服务器上的资源,如图片、音频和视频文件。在本文中,我们将探讨防盗链的概念和原理,并结合 Spring Boot 提供一个完整的可运行示例。
Referer
字段来实现。如果请求的来源不是允许的域名,则拒绝该请求。除此之外,还可以结合 Token
和时间戳进一步提高安全性,确保链接只能在一定时间内有效。
src
├── main
│ ├── java
│ │ └── com.demo
│ │ ├── DemoApplication.java
│ │ ├── filter
│ │ │ ├── StaticResourceFilter.java
│ │ │ ├── TokenValidator.java
│ │ │ └── TimeValidator.java
│ └── resources
│ └── static
│ └── images
└──711815.jpeg
package com.et;
import com.et.filter.StaticResourceFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public FilterRegistrationBean<StaticResourceFilter> staticResourceFilter() {
FilterRegistrationBean<StaticResourceFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new StaticResourceFilter());
registrationBean.addUrlPatterns("/images/*");
return registrationBean;
}
}
package com.et.filter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class StaticResourceFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// validate Referer
String referer = httpRequest.getHeader("Referer");
String allowedDomain = "http://localhost:8088";
if (referer == null || !referer.startsWith(allowedDomain)) {
httpResponse.getWriter().write("403 Forbidden: Hotlinking not allowed");
return;
}
// validate Token
if (!TokenValidator.validateToken(httpRequest, httpResponse)) {
return;
}
// validate Timestamp
if (!TimeValidator.validateTimestamp(httpRequest, httpResponse)) {
return;
}
chain.doFilter(request, response);
}
}
package com.et.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TokenValidator {
public static boolean validateToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
String token = request.getParameter("token");
String validToken = "your-predefined-token"; //set your predefined token here
if (token == null || !token.equals(validToken)) {
response.getWriter().write("403 Forbidden: Invalid Token");
return false;
}
return true;
}
}
package com.et.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Instant;
public class TimeValidator {
private static final long ALLOWED_TIME_DIFF = 300; // offset in seconds( 300 seconds)
public static boolean validateTimestamp(HttpServletRequest request, HttpServletResponse response) throws IOException {
String timestampStr = request.getParameter("timestamp");
if (timestampStr == null) {
response.getWriter().write("403 Forbidden: Missing Timestamp");
return false;
}
try {
long timestamp = Long.parseLong(timestampStr);
long currentTimestamp = Instant.now().getEpochSecond();
if (Math.abs(currentTimestamp - timestamp) > ALLOWED_TIME_DIFF) {
response.getWriter().write("403 Forbidden: Timestamp Expired");
return false;
}
} catch (NumberFormatException e) {
response.getWriter().write("403 Forbidden: Invalid Timestamp");
return false;
}
return true;
}
}
.jpeg
放入 src/main/resources/static/images
文件夹中。
curl -X GET "http://localhost:8088/static/example.jpg?token=your-predefined-token×tamp=$(date +%s)" -H "Referer: http://localhost:8088"
403 Forbidden: Hotlinking not allowed
。403 Forbidden: Invalid Token
。403 Forbidden: Timestamp Expired
。