这是一个使用阿里云对象存储的文件,在当前网站内可以正常显示,右键复制图片地址,在当前浏览器打开,也可以正常下载。但是当复制这个图片地址,然后在新的浏览器打开,神奇的事情发生了。

为什么会发生这种事情呢?
这就不得不提一下阿里云对象存储的防盗链原理了。
防盗链功能通过设置Referer白名单以及是否允许空Referer,限制仅白名单中的域名可以访问您Bucket内的资源。OSS支持基于HTTP和HTTPS header中表头字段Referer的方法设置防盗链。
是否进行防盗链验证的具体场景如下:
- 仅当通过签名URL或者匿名访问Object时,进行防盗链验证。
- 当请求的Header中包含
Authorization
字段,不进行防盗链验证。防盗链通过请求Header中的Referer地址判断访问来源。当浏览器向Web服务器发送请求的时候,请求Header中将包含Referer,用于告知Web服务器该请求的页面链接来源。OSS根据浏览器附带的Referer与用户配置的Referer规则来判断允许或拒绝此请求,如果Referer一致,则OSS将允许该请求的访问;如果Referer不一致,则OSS将拒绝该请求的访问。例如,某个Bucket设置了Referer为https://10.10.10.10.com:
- 用户A在https://10.10.10.10.com 嵌入test.jpg图片,当浏览器请求访问此图片时会带上https://10.10.10.10.com 的Referer,此场景下OSS将允许该请求的访问。
- 用户B盗用了test.jpg的图片链接并将其嵌入https://127.0.0.1.com ,当浏览器请求访问此图片时会带上https://127.0.0.1.com 的Referer,此场景下OSS将拒绝该请求的访问。
以上引用阿里云对象存储的防盗链说明,可以看出,阿里云对象存储的防盗链验证机制非常简单,
就是在请求头中加入referer:{allow-string} 。关于当前网站静态资源结构说明,请看使用typora结合picgo和alioss编写markdown,发表blog ,alioss设置的权限是公共读,并开启oss验证,这样就需要在请求资源的时候,给响应加上referer。下面涉及nginx的部分配置,需要一定的nginx基础知识。
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ { add_header Referer https://www.xxxx.cn; expires 30d; access_log off; }
nginx匹配静态资源,并将静态资源响应加上referer响应头。这样当浏览器再去请求这个资源的时候,就可以带着我们返回的响应头进行请求了。
或者还有更加简单但是可能会出意外的方式:Authorization: xxxxxx。这个方式和上面是同样的原理,就是需要请求头中存在Authorization字段。但是请注意:这种验证方式,相当于让网站自己去做的验证,用户登录后,相当于对用户身份进行了验证,即避免了一大部分的盗链情况,如果非法用户伪造authorization字段,虽然无法正常登录,但是可以轻松盗图,仔细一想,非法用户也不用登录啊,只是偷一个图片而已。正常登录的用户,访问图片不算盗图,非法用户轻松盗图而不必真正登录.....哎,这个设计是如此的愚蠢
.(@阿里工程师)。
有了破防原理,下面讲讲各种爬虫应该如何破防盗链。
python
import requests url1='https://baijiahao.baidu.com/s?id=1697976987508204190&wfr=spider&for=pc' #添加请求头 headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE', 'Referer': 'allow-string' } response_1=requests.get(url1, headers=headers)
php
function http_post($sUrl, $aHeader, $aData){ $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_URL, $sUrl); curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($aData)); $sResult = curl_exec($ch); if($sError=curl_error($ch)){ die($sError); } curl_close($ch); return $sResult; } $url = 'https://www.example.com; $header = array('User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36','Referer:allow-string'); $data = array(); $data = http_post($url,$header,$data);
go
package main import ( "fmt" "io/ioutil" "net/http" "os" "encoding/json" ) func main() { //生成client 参数为默认 client := &http.Client{} //生成要访问的url url := "http://somesite/somepath/" //提交请求 reqest, err := http.NewRequest("GET", url, nil) //增加header选项 reqest.Header.Add("Cookie", "xxxxxx") reqest.Header.Add("User-Agent", "xxx") reqest.Header.Add("X-Requested-With", "xxxx") reqest.Header.Add("Referer", "allow-string") if err != nil { panic(err) } //处理返回结果 response, _ := client.Do(reqest) defer response.Body.Close()
java
HttpResponse response = null; HttpGet get = new HttpGet(url); get.addHeader("Accept", "text/html"); get.addHeader("Accept-Charset", "utf-8"); get.addHeader("Accept-Encoding", "gzip"); get.addHeader("Accept-Language", "en-US,en"); get.addHeader("Referer", "allow-string"); get.addHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.160 Safari/537.22"); response = client.execute(get); HttpEntity entity = response.getEntity(); Header header = entity.getContentEncoding(); if (header != null) { HeaderElement[] codecs = header.getElements(); for (int i = 0; i < codecs.length; i++) { if (codecs[i].getName().equalsIgnoreCase("gzip")) { response.setEntity(new GzipDecompressingEntity(entity)); } } } return response;
Comments