2010年08月6日 16:16

下载权限控制机制

要对下载的权限进行精确的控制(防止盗链,防止迅雷吸血,下载扣除积分等虚拟货币),以前接触的方法有几种:
1、通过rewrite不断地更改下载文件的url,并插入很多无意义的字符;
2、验证下载链接的来路,或者cookie;
3、通过服务器端程序(例如一个php文件),open文件,读取内容然后返回给客户端。
第一种方法很笨,而且吃力不讨好;
第二种方法很容易破解,因为referer和cookie都是客户端发出的,能够方便地伪造,而且迅雷对此已经是轻车熟路;
第三种方法是可行的有效的,所有的文件都经过一个程序读取并发送,在读取之前可以有效的验证权限,但是下载过程中始终要占用一个cgi线程,而且一般cgi语言的IO性能都不好,速度很慢,占用了服务器的大量资源,导致总体效率极其低下,难以大规模运用。
为此我研究了一下csdn下载频道的实现机制。
csdn下载频道能够有效的验证权限,扣除积分,而且不排斥迅雷等下载客户端,同一个用户下载同一个文件也不会重复扣除积分,而且下载时始终没有暴露文件的真实地址,同一个下载URL到了别的地方也完全不可用,可以说是实现得比较理想的。
我选择了一个文件进行测试,下载的url是: http://dldx.csdn.net/fd.php?i=573624740728082&s=4fc2353ca769a0ebd9237b6f98791679
这个url向文件存储服务器上的fd.php文件发送了两个经过加密的参数,里面应该包含有用户登录信息(用户ID和sid)和目标文件的ID号。
用迅雷下载这个文件,截获返回的头信息:
Host: dldx.csdn.net
Pragma: no-cache
Range: bytes=0-
Referer: http://d.download.csdn.net/down/2474072/waf9898
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; )
HTTP/1.1 206 Partial Content
Server: nginx/0.7.65
Date: Tue, 22 Jun 2010 07:08:21 GMT
Content-Type: “application/octet-stream; charset=utf-8″
Content-Length: 667747
Last-Modified: Mon, 21 Jun 2010 23:45:02 GMT
Connection: keep-alive
Content-Disposition: attachment; filename=”DNF%E6%82%A0%E6%82%A05%5B1%5D.7.rar”
Expires: 0
Cache-Control: must-revalidate, post-check=0, pre-check=0
Content-Range: bytes 0-667746/667747
这里面始终没有暴露目标文件的真实路径,不是一般下载系统所使用的header重定向的方式。而且有一个重命名的信号。服务器使用的程序是nginx/0.7.65。
根据这些信息,在google搜索到这篇文章:http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/
显然,csdn就是使用了文中所说的nginx X-Accel-Redirect。
解释一下整个过程:
步骤1,客户端请求http://dldx.csdn.net/fd.php ,并传递相关信息;
步骤2,fd.php根据所传递的信息判断出访问者的身份和所请求的资源,然后应该验证了客户端的IP,进一步判断其权限。如果这个客户端有权下载此文件,则在HTTP header加入X-Accel-Redirect: (文件的真实路径),并加上head Content-Type和Content-Disposition:;
步骤3,nginx得到fd.php的回应后发现带有X-Accel-Redirect的header,那么根据这个头记录的路径信息打开目标文件;
步骤4,nginx把打开文件的内容返回给客户端。
这样所有的权限检查和积分扣除等操作都可以在步骤2内完成,而且fd.php返回带X-Accel-Redirect的头后,其执行已经终止,剩下的传输文件的工作由nginx 来接管,同时X-Accel-Redirect头的信息被nginx删除,不会返回给客户端,也就不会暴露(实际上可以把目标文件存储在不能经由web访问的目录),并且由于nginx在打开静态文件上使用了 sendfile(),其IO效率非常高,比php的IO要快上N++倍。
这是一种优雅,有效,高效的实现方案。
因为没有架设过nginx服务器,我希望能在apache实现这个功能,于是查找了一下有没有类似的mod,果然查找到了一个mod_xsendfile:http://tn123.ath.cx/mod_xsendfile/ ,其实现机制与nginx的X-Accel-Redirect基本相同。
下载之后在本机测试。
1、加载mod_xsendfile。将文件 mod_xsendfile.so 移动到 apache/modules 目录下,将以下内容添加到httpd.conf中
LoadModule xsendfile_module [...]

2010年08月2日 11:15

中等规模相册的上传存储机制初探

这里对中等的定义是:图片文件所占空间在1TB–99TB。
这个机制是研究了人人网等一些UGC网站所得出的方案。
以4台服务器为模型:相册所属的主站服务器A、主站所用的Mysql服务器M、接收并处理上传文件的服务器B、最终存储图片文件并提供http下载的服务器C。实际应用中C应为多台服务器分布式存储。
首先,上传图片的入口在A上。那么,上传表单所属的html文件应该存储在A还是B呢?第一感觉应该是在A上,然后表单的action指向B,这样就可以直接把文件数据提交到B。但是事实上,我们通常会在相册中使用ajax提交表单,如果表单在A上,而数据提交到B,就会造成跨域的问题。所以,我们把这个表单部署在B上,通过同一个根域的cookie和存储在M上的session数据来验证用户身份。
B的基本任务是:验证,去重,处理,存储。
验证:B接收到数据以后,先判断文件大小和Content-Type、扩展名等是否符合要求。
去重:去重基本被大多数人忽略,我想是因为对很多网站来说短期内可以承受,但是实际的经验是,重复图片会占到50%以上的惊人比例,一些流行的图片会被不断地上传。而且这里还关系到一个审核的问题,比如一些流行的黄图或者不和谐的政治图片会被频繁上传,如果没有去重机制,会加大审核的工作量。所以,有必要对上传的每个文件取得一个二进制的MD5值,存储到数据库里。这里存入的不是M上的数据库,而是B自带的数据库。上传来的文件,如果MD5重复,就直接返回已经存在的图片路径;如果不重复,就插入新的数据,返回新的路径;如果该图片已经被判定违规,就返回一个错误信号。
处理:生成缩略图,可能是不同分辨率的缩略图。如果有需要,还得添加水印。
存储:存储分三部分:M上的数据库有相册图片相关的信息需要存入;B上的数据库也有文件信息需要存入;最后还得把文件存到C上,才能提供http下载。第一第二步不再叙述,第三步初步决定使用ftp(直接把上传的流写入ftp,B、C通信速度应该很快),也可使用专门的分布式存储系统来实现。路径可设计为 http://域名/分布式目录/20100730(年月日)/1355(时分)/large(不同大小)_(随机码).jpg,把这个路径分解以后存储到M即可。

2010年06月24日 13:51

不要在网上随便贴自己的照片,看这只兔子的教训

2010年06月11日 12:32

flash小游戏:堆积木 手不要抖 要放正啊

挺好玩的  每个人玩法都不一样 发挥你的想象力和创造力吧
总之就是要把所有的积木堆起来  一定时间内不要塌了就行了

2010年06月10日 23:06

一个好玩的东西:人脸生成器

可以组合出各种人脸。。
链接:http://kuigg.com/flash/face.swf
这是黄薇同学做的自画像: 

黄薇做的自画像


以下
随便玩玩。。

2010年06月10日 22:16

你应该知道的PHP十件事

1.使用ip2long() 和long2ip()函数来把IP地址转化成整型存储到数据库里。
这种方法把存储空间降到了接近四分之一(char(15)的15个字节对整形的4个字节),计算一个特定的地址是不是在一个区段内也更简单了,而且加快了搜索和排序的速度(虽然有时仅仅是快了一点)。
2.在验证Email地址的时候使用checkdnsrr() 函数验证域名是否存在。
这个内置函数能够确认指定的域名能够解析成IP地址。该函数的PHP 文档的用户评论部分有一个简单的用户自定义函数,这个函数基于checkdnsrr(),用来验证 email 地址的合法性。对于那些认为自己的Email地址是user@www163.com而不是user@163.com的家伙们,这个方法可以很方便的抓住他们。
3.如果你使用的是PHP 5和MySQL 4.1 或者更高的版本,考虑用mysqli_* 系列函数。
一个很好的功能就是你可以使用预处理语句,如果你在维护一个数据库密集型站点,这个功能能够加快查询速度。一些评估分数。
4.学会爱上三元运算符。
5.如果你在项目中感觉到有可复用的部分,在你写下一行代码前先看看PEAR中是否已经有了。
很多PHP程序员都知道 PEAR 是一个很好的资源库,虽然还有很多程序员不知道。这个在线资源库包含了超过400个可以复用的程序片段,这些程序片段你可以立即用刀你的程序里。除非说你的项目真的是非常特别的,你总能找到帮你节省时间的 PEAR包。
6.用 highlight_file()来自动的打印出格式化的很漂亮的源代码。
如果你在留言板、IRC 这些地方寻求一个脚本的帮助的话,这个函数用起来非常的顺手。当然了,要小心不要意外的泄露出你的数据库连接信息和密码等。
7.使用 error_reporting(0)函数来防止用户看到潜在的敏感错误信息。
在理想情况下,发布服务器应该在php.ini 里完全禁止。但是如果你用的是一个共享的 web 服务器的话,你没有自己的 php.ini 文件,那么这种情况下你最好的选择就是在所有脚本的第一行前加上 error_reporting(0);(或者使用 require_once() 方法)。这就能够在出错的时候完全屏蔽敏感的SQL查询语句和路径名。
8.在网数据库中存储很大的字符串之前使用 gzcompress() 和 gzuncompress() 来显式的压缩/解压字符串。
这个PHP内置函数使用 gzip 算法,可以压缩普通文本达 90%。在我每次要读写BLOB类型的字段的时候都使用这些函数。唯一额例外就是当我需要全文检索的时候。
9.通过“引用”传递参数的方法从一个函数中得到多个返回值。
就像三元运算符一样,大部分受过正式编程训练的程序员都知道这个技巧。但是那些 HTML 背景大于 Pascal 背景的程序员都或多或少的有过这样的疑问“在仅能使用一次 return 的情况下,从一个函数里返回多个值?”答案就是在变量前加上一个 “&” 符号,通过“引用”传递而非“值”传递。
10.完全理解“魔术引号”和 SQL 注入的危险性。

2010年06月10日 21:00

收集一些常用的正则表达式

“^\d+$”  //非负整数(正整数 + 0)
“^[0-9]*[1-9][0-9]*$”  //正整数
“^((-\d+)|(0+))$”  //非正整数(负整数 + 0)
“^-[0-9]*[1-9][0-9]*$”  //负整数
“^-?\d+$”    //整数
“^\d+(\.\d+)?$”  //非负浮点数(正浮点数 + 0)
“^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$”  //正浮点数
“^((-\d+(\.\d+)?)|(0+(\.0+)?))$”  //非正浮点数(负浮点数 + 0)
“^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$”  //负浮点数
“^(-?\d+)(\.\d+)?$”  //浮点数
“^[A-Za-z]+$”  //由26个英文字母组成的字符串
“^[A-Z]+$”  //由26个英文字母的大写组成的字符串
“^[a-z]+$”  //由26个英文字母的小写组成的字符串
“^[A-Za-z0-9]+$”  //由数字和26个英文字母组成的字符串
“^\w+$”  //由数字、26个英文字母或者下划线组成的字符串
“^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$”    //email地址
“^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$”  //url
/^(d{2}|d{4})-((0([1-9]{1}))|(1[1|2]))-(([0-2]([1-9]{1}))|(3[0|1]))$/   //  年-月-日
/^((0([1-9]{1}))|(1[1|2]))/(([0-2]([1-9]{1}))|(3[0|1]))/(d{2}|d{4})$/   // 月/日/年
“^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$”   //Emil
/^((\+?[0-9]{2,4}\-[0-9]{3,4}\-)|([0-9]{3,4}\-))?([0-9]{7,8})(\-[0-9]+)?$/     //电话号码
“^(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5])$”   //IP地址
匹配中文字符的正则表达式: [\u4e00-\u9fa5]
匹配双字节字符(包括汉字在内):[^\x00-\xff]
匹配空行的正则表达式:\n[\s| ]*\r
匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/
匹配首尾空格的正则表达式:(^\s*)|(\s*$)
匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
匹配网址URL的正则表达式:^[a-zA-z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
匹配国内电话号码:(\d{3}-|\d{4}-)?(\d{8}|\d{7})?
匹配腾讯QQ号:^[1-9]*[1-9][0-9]*$
元字符及其在正则表达式上下文中的行为:
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个后向引用、或一个八进制转义符。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的Multiline 属性,^ 也匹配 ’\n’ 或 ’\r’ 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了 RegExp 对象的Multiline 属性,$ 也匹配 ’\n’ 或 ’\r’ 之前的位置。
* 匹配前面的子表达式零次或多次。
+ 匹配前面的子表达式一次或多次。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。? 等价于 {0,1}。
{n} n 是一个非负整数,匹配确定的n 次。
{n,} n 是一个非负整数,至少匹配n 次。
{n,m} m 和 n 均为非负整数,其中n [...]

2010年06月10日 06:37

“西化”的名词之争

这些年,几本“中国”如何的书——有“说不”和“不高兴”的,有要“站起来”的(不好乱扣“帽子”,暂用“站起派”称呼这些人吧),总的来说要扬中国人和中国文化的志气,灭西方人和西方文化的威风。

为了不犯和“站起派”一样的错误,我们设身处地站在“站起派”立场思考一下:他们也不会“全盘否认”中国有学习外国文化的必要,他们反对的是“崇洋媚外”“全盘西化”。不当洋奴,态度旗帜鲜明,但是操作上有些难度:怎样才算“站起来”,学多少西方文化才不是洋奴——10%?30%?还是49%以下?就像某些国有公司绝对控股的要求一样。反过来,要批判“全盘西化”的洋奴,说他们100%中国文化都不要,也不符合实际情况。

2010年06月10日 00:37

在人人网发现典型五毛活体

看来认为我们能吃饱饭是因为“党让你吃饱了”的人还不少啊。
施鑫焱淼 2010-06-10 00:25

这个就是吃人家的喝人家的还说人家没给你吃撑,政权都希望稳固,就像人都想着吃饱,在谁的土地上说谁的话,毕竟共产党让你吃饱了,资本主义那发达了这么久还有人饿肚子,真的不容易了

2010年06月9日 08:03

wordpress中默认的发邮件用户和地址的修改方法

wordpress中默认的发邮件用户和地址是 WordPress <wordpress@kuigg.com>
我想把它修改成 kuiGG <i@kuigg.com>
研究了一下,找到修改的方法:
需要修改的文件是 wp-includes\pluggable.php
搜索“wordpress@”  有两处  都改成 “i@”
还有 找到以下代码

1
2
3
if ( !isset( $from_name ) ) {
  $from_name = ‘WordPress’;
 }

修改成

1
2
3
if ( !isset( $from_name ) ) {
  $from_name = ‘kuiGG’;
 }