[基于Asp.Net的防盗链](三) 实践
上文介绍了思路,本篇根据 赞美诗网(http://www.zanmeishi.com/) 的实际情况,来加入防盗链功能。
赞美诗网 现状:启动了2个域名:
http://www.zanmeishi.com/ 主站 所有的页面,都在主站
http://file.zanmeishi.com/ 文件 文件站只提供 相应的 歌手形象照片,专辑封面,试听,下载,等的文件访问。
分别对应了 两个 web项目。 Zms.Web 和 Zms.File
文件存储方式如下, 在表中 (如歌曲表Song) 记录 试听和下载的文件位置。如:
试听: 表中记录 PlayUrl = /歌手/专辑/歌曲.wma
试听: 表中记录 DownUrl = /歌手/专辑/歌曲.mp3
文件存在服务器中某目录。。。。
在 Zms.File 直接通过传递 歌曲(Song) 的 Id, 得到 文件保存的路径。。 然后读取文件,发送。。。
也就是说,我的文件都是通过 Asp.Net 程序发送的。。。采用了url重写,所以,你会得到像下载的地址:
http://file.zanmeishi.com/play/150/634/11864.wma
http://file.zanmeishi.com/play/150/634/11864.mp3
这个地址很简单,就是 /歌手Id/专辑Id/歌曲Id.wma 为什么要这样重写呢?
因为要考虑到,别人批量采集你的文件。。 所以,我加了 歌手,专辑,歌曲Id的限定。。
然后在 主站中 用一个方法来生成 试听 和 下载地址。。 Urls.Play Urls.Down
项目的情况基本上是这样, 现在跟据这个情况,结合上文说的思路,我们来对 赞美诗网 做防盗链吧。。。
看看,我们怎么加密这个内容,
类型,标识,浏览器信息,IP,这都是很简单的,直接可以得到。。
比较麻烦的是,过期时间怎么处理。 生成hash后,这个hash要在指定的时间内有用,过期无效。。
我想到的办法是这样。。 先设定一个时间值,比如 60分钟。
得到当前时间,而且要得到一个累加的值,我使用当前年的1月1日0点0分0秒 做基数。
用当前 时间 去减去 基数,得到 总分钟数。。。。
然后用这个 总分钟数 对 60 取余, 再减去这个余数。。。。。
这样,我得到的这个值, 总是 60 的倍数(或说,是你设定时间的倍数)
比如,现在离2010年1月1日0点0分 相差的分钟数是 543 (随意值)。。
那这样算下来,得到的 值等于 543-(543%60) = 540 然后用这个值,去放到hash里面。
也就是说,得到的hash 在 540-600分钟之内,是有效的。 当到了 总分钟数到了 600的时候, 基数变成了 600。
所以,在之前的hash就没法通过验证了。就失效了。。
放代码,以下是生成hash的代码。。(伪代码):
2 /// 生成 验证Hash
3 /// </summary>
4 /// <param name="type">类型</param>
5 /// <param name="id">标识</param>
6 /// <returns></returns>
7 public static string Hash(string type, string id)
8 {
9 //得到:当前时间,和,减于基数的TimeSpan
10 var now = DateTime.Now;
11 var ts = now - new DateTime(now.Year, 1, 1, 0, 0, 0);
12
13 //得到:总分钟数,余数,参于hash的值 (也可以按秒计算,随意)(我这是60分钟)
14 int total = (int)ts.TotalMinutes;
15 int mod = total % 60;
16 int time = total - mod;
17
18 //得到:浏览器信息,IP地址
19 string agent = Request.UserAgent;
20 string ip = Request.UserHostAddress;
21
22 //Hash格式 (可以把几个参数调换位置 或 加一些混淆内容)(如果不要限制某一项,把参数去掉就可以了)
23 string encrypt = "{type}{id}{time}{agent}{ip}";
24
25 //替换相应的内容
26 encrypt = encrypt.Replace("{type}", type);
27 encrypt = encrypt.Replace("{id}", id);
28 encrypt = encrypt.Replace("{time}", time.ToString());
29 encrypt = encrypt.Replace("{agent}", agent);
30 encrypt = encrypt.Replace("{ip}", ip);
31
32 //算Md5 并转成小写
33 return Md5(encrypt).ToLower();
34 }
生成Hash的方法完成了。。 接下来,我们来包装URL。。
我把得到URL封装了两个方法,比如 Urls.Play Urls.Down
我使用Play和Down做为类型,使用Id做为标识,这样来生成url
在这两个方法中,我把得到的URL加上一个参数, 就是生成的Hash 加在Url上。。所以,得到如下的地址:
http://file.zanmeishi.com/play/156/600/11398.wma?hash=804f14072e779121b00d340994f5e6d9
http://file.zanmeishi.com/down/156/600/11398.mp3?hash=a92ccb3f582fe9d8703377a08aedec20
具体生成Url的方法,我就不贴代码了,你可以按你自己的方法,来加上hash就可以了。
然后我在处理文件的时候, 得到参数 hash ,然后又生成一个新的Hash,再去验证两个hash是否相等。。。
伪代码如下:
2 string file = Server.MapPath(".......");
3
4 //得到类型和标识和Hash
5 string type = "Play"; //注意,这个和你生成Hash时候使用的 type 要一致
6 int id = Convert.ToInt32(Request.QueryString["SongId"]);
7 string hash = Request.QueryString["SongId"];
8
9 //生成新的hash
10 string newHash = Link.Hash(type, id); //刚刚写的方法
11
12 //判断两个hash是否一致,不区分大小写
13 if (string.Compare(newHash, hash, true) != 0)
14 {
15 //不一致,提示错误
16 Response.Write("请不要盗链本站文件!");
17 Response.End();
18 }
19 else
20 {
21 //输出文件
22 Response.WriteFile(file);
23 }
到这里就差不多了。。 可以试试了。。。。
注:如果你想取消某一项的限制,只需要在加密内容中去掉相应的参数即可。
测试请直接到 赞美诗网 http://www.zanmeishi.com
当前配置:类型+标识+时间(60分钟)+IP限制, 没有限制浏览器。。。
为什么不加浏览器限制呢,是因为我提供在线试听, 播放器和浏览器的 http header 是不同的,
所以,浏览时得到的hash, 在播放器中根本没法通过验证。。所以,取消了此限制。。。。。。
你可以根据自己的需求来增减相应的限制。。
补充上文忘记写的内容, 对于音乐试听,只判断来源页,是没办法防止的,因为播放器调用你的文件,是不携带,来源页的。。
所以,如果只判断来源,是没办法防止百度MP3来抓我的文件,提供给网友试听。。
所以,这里的IP限制很有作用,也就是说,百度抓到的Url,只有百度服务器的IP可以用。
这样,就算百度MP3提供试听,其它人是访问不了我的文件的了。。
这样也可以限制下载软件,因为下载软件和浏览器携带的Http header 是不同的。。
用户在浏览网页是得到的Hash,去使用下载软件下载,是无法通过验证的。。
相对来说,这种方法可以防掉大部分的盗链,采集批量下载。。 可以只允许用户在浏览时候访问文件。。也防止URL被传播或是盗用。
其实商业的防盗链软件,差不多是类似的思路,不同的只是在加密内容和验证方式上做文章。。。
本文是使用Asp.Net来实现, 不需要使用isapi之类的底层方式,也同样可以实现很强的防盗链。
最重要的是:简单且免费~~
下篇剧透:应用此方式,使用 HttpModule 编写成 Asp.Net 组件。以实现通用。。将来在自己的项目中都会使用此组件防盗链。
枫知秋 原创文章 转载请声明 并 写明出处
http://jorise.cnblogs.com/