防盜鏈的實現原理以及相應的解決方法
阿新 • • 發佈:2018-11-02
- 我的實現防盜鏈的做法,也是參考該位前輩的文章。基本原理就是就是一句話:通過判斷request請求頭的refer是否來源於本站。(當然請求頭是來自於客戶端的,是可偽造的,暫不在本文討論範圍內)。
- 首先我們去了解下什麼是HTTP Referer。簡言之,HTTP Referer是header的一部分,當瀏覽器向web伺服器傳送請求的時候,一般會帶上Referer,告訴伺服器我是從哪個頁面連結過來的,伺服器籍此可以獲得一些資訊用於處理。比如從我主頁上鍊接到一個朋友那裡,他的伺服器就能夠從HTTP Referer中統計出每天有多少使用者點選我主頁上的連結訪問他的網站。(注:該文所有用的站點均假設以
假如我們要訪問資源:http://blog.csdn.net/Beacher_Ma 有兩種情況:
1. 我們直接在瀏覽器上輸入該網址。那麼該請求的HTTP Referer 就為null
2. 如果我們在其他其他頁面中,通過點選,如 http://www.csdn.net 上有一個 http://blog.csdn.net/Beacher_Ma 這樣的連結,那麼該請求的HTTP Referer 就為http://www.csdn.net - 知道上述原理後,我們可以用Filter去實現這個防盜鏈功能。網上的做法多是用列舉的方式去做的,而我這裡是用正則去做,相對比較靈活點,另外,我效仿了Spring的filter做法,加了個shouldBeFilter的方法,考慮到,比如假如你要攔截*.Action的一部分方法,而不是全部時,我們就可以先看看請求的URL是否shouldBeFilter,如果不是的話,那麼就直接放行,在效率上有所提高。廢話不說,直接上程式碼吧。
//防盜鏈filter
public class PreventLinkFilter implements Filter {
private static Logger logger = LoggerFactory
.getLogger(PreventLinkFilter.class);
// 限制訪問地址列表正則
private static List<Pattern> urlLimit = new ArrayList<Pattern>();
// 允許訪問列表
private static List<String> urlAllow = new ArrayList<String>();
// 錯誤地址列表
private static String urlError = "";
// 必須過Filter的請求
protected boolean shouldBeFilter(HttpServletRequest request)
throws ServletException {
String path = request.getServletPath();
for (int i = 0; i < urlLimit.size(); i++) {
Matcher m = urlLimit.get(i).matcher(path);
if (m.matches()) {
logger.debug("當前的Path為{}" + path + "必須進行過濾");
return true;
}
}
return false;
}
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (null == httpRequest || null == httpResponse) {
return;
}
// 放行不符合攔截正則的Path
if (!shouldBeFilter(httpRequest)) {
chain.doFilter(request, response);
return;
}
String requestHeader = httpRequest.getHeader("referer");
if (null == requestHeader) {
httpResponse.sendRedirect(urlError);
return;
}
for (int i = 0; i < urlAllow.size(); i++) {
if (requestHeader.startsWith(urlAllow.get(i))) {
chain.doFilter(httpRequest, httpResponse);
return;
}
}
httpResponse.sendRedirect(urlError);
return;
}
public void init(FilterConfig fc) throws ServletException {
logger.debug("防盜鏈配置開始...");
String filename;
try {
filename = fc.getServletContext().getRealPath(
"/WEB-INF/classes/preventLink.properties");
File f = new File(filename);
InputStream is = new FileInputStream(f);
Properties pp = new Properties();
pp.load(is);
// 限制訪問的地址正則
String limit = pp.getProperty("url.limit");
// 解析字串,變成正則,放在urlLimit列表中
parseRegx(limit);
// 不受限的請求頭
String allow = pp.getProperty("url.allow");
// 將所有允許訪問的請求頭放在urlAllow列表中
urlAllow = parseStr(urlAllow, allow);
urlError = pp.getProperty("url.error");
} catch (Exception e) {
e.printStackTrace();
}
}
private void parseRegx(String str) {
if (null != str) {
String[] spl = str.split(",");
if (null != spl) {
for (int i = 0; i < spl.length; i++) {
Pattern p = Pattern.compile(spl[i].trim());
urlLimit.add(p);
}
}
}
}
private List<String> parseStr(List<String> li, String str) {
if (null == str || str.trim().equals("")) {
return null;
}
String[] spl = str.split(",");
if (null != spl && spl.length > 0) {
li = Arrays.asList(spl);
}
return li;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
檔案/WEB-INF/classes/preventLink.properties
用於限制的url正則,用逗號分隔多個(在這裡我攔截了諸如 /csdn/index!beacher_Ma.action,/csdn/index!beacher_Ma.action?adsfdf)
url.limit=/.+/index/!.+//.action.*,/index/!.+.action?.+
這裡是Http Refer是否以指定字首開始,前兩個是本地除錯用的。。 url.allow=http://127.0.0.1,http://localhost,http://www.csdn.net
這裡是被盜鏈後,response到以下的錯誤頁面 url.error=http://www.csdn.net/error.html
參考文章:http://shen198623.javaeye.com/blog/243330
這篇文章是攔截所有的請求的,他fileter中的url-pattern是/*,這樣的話,連/css /jpg等都話被filter攔截到,要麼在裡面進行shouldBeFilter的判斷,要麼就在url-pattern中縮寫攔截範圍,這個要看具體你要攔截什麼樣的請求,另外圖片防盜鏈,下載反盜鏈也是一樣的原理的。