“盗链” 说白了就是利用别人网站的资源连接放在本身的站点,在未经容许的状况下去获取别人网站里面的图片或者视频等资源,致使资源全部者的网站的流量费用增长或收入减小,为了防止资源连接随意被人盗用的手段被称为 “防盗链”。javascript
咱们先来模拟一下 “盗链” 场景,在本地启动服务运行 hotlinking.html
文件,并在文件中盗用百度视频的图片资源,看看效果。html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>盗链</title>
</head>
<body>
<img src="http://c.hiphotos.baidu.com/video/pic/item/e61190ef76c6a7ef8891a7c9f1faaf51f2de66ad.jpg">
</body>
</html>复制代码
咱们经过 http-server
来启动服务器访问 hotlinking.html
,使用 http-server
需全局安装。java
http-server install -g浏览器
在服务中打开 hotlinking.html
后咱们发现图片并非咱们盗用连接的资源,而是变成了下面这张图片。bash
这张图用来提醒咱们盗用了别人资源,是由于百度的服务器作了防盗链处理,若是全部盗用别人的资源都变成这样,盗用也就没有实际意义了,咱们本篇就经过 NodeJS 来实现防盗链处理,用来保护本身站点的资源。服务器
在本地的 hosts 文件中加入两个域名:async
在根目录建立文件夹 public
并存入两张图片,success.png
是正常请求的图片资源,error.png
是通过防盗链处理后返回的图片资源,两张图片以下。学习
正常返回的图片资源 success.png:网站
防盗链处理后返回的图片资源 error.png:
在页面当中经过 img
标签分别访问 shen.com、panda.com 和 localhost 域下的 success.png
文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>盗链</title>
</head>
<body>
<img src="http://panda.com:3000/success.png">
<img src="http://shen.com:3000/success.png">
<img src="http://localhost:3000/success.png">
</body>
</html>复制代码
在写服务端代码以前须要介绍两个重要的请求头:
其实资源防盗就是设置白名单,经过检测 referer
是否在白名单中,若是在则正常返回资源,不存在则返回通过防盗链处理的资源。
referer
的写法为 referered
,因此为了兼容旧版本协议应该作兼容处理。
// 引入依赖
const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("mz/fs");
const server = http.createServer(responseImages); // 建立服务器
let static = path.resolve(__dirname, "public"); // 静态资源目录
let whiteList = ["shen.com"]; // 白名单
async function responseImages(req, res) {
// 解析 url 中的文件目录处理成绝对路径
let p = path.join(static, url.parse(req.url).pathname);
// 检测文件路径是否合法,不合法直接返回 Not Found
let isExist = await fs.exists(p);
if (isExist) {
// 获取 referer
let refer = req.headers["referer"] || req.headers["referered"];
// 存在 referer 继续检测
if (refer) {
// 请求资源存在 referer,作防盗链处理
let referHost = url.parse(refer).hostname;
let host = req.headers["host"].split(":")[0];
// 当访问源的域和资源所在的域不是同一个域,作防盗链处理
if (referHost !== host) {
let isInWhiteList = whiteList.includes(refer);
p = isInWhiteList ? p : path.join(static, "error.png");
}
}
// 第一次访问请求页面 index.html,不存在 referer,将静态资源返回
// 第二次访问请求图片资源,若是 referer 和资源所本就是同一个域,直接将资源返回
fs.createReadStream(p).pipe(res);
} else {
res.statusCode = 404;
res.end("Not Found");
}
}
server.listen(3000, () => {
console.log("server start 3000");
});复制代码
其实上面的服务器是 shen.com、panda.com 和 localhost 所共用的,只是经过不一样的域名访问。
启动服务器,而后经过 localhost:3000 访问,此时因为与 shen.com 和 panda.com 为不一样域,因此只有第三张图片返回 success.png
。
经过 shen.com:3000 访问,因为存在白名单中,因此三张图片都返回 success.png
。
经过 panda.com:3000 访问,因为 shen.com 在不一样域,因此没有返回 success.png
。
在上面咱们利用本地服务实现了一个最基本的防盗链,思路就是 referer
与资源同域,正常返回,不一样域检测白名单,在真实的开发场景可能会更细化,更复杂一些,其实整个防盗链实现的核心就是利用 HTTP 的 referer
和 host
请求头作检测,但愿经过本篇的学习,你们能够对资源防盗链有所了解,并在后面开发相似功能时提供思路。