上回提到的防盗链方式是在strings上加上key和time,uri本身是不变的,这种方式其实现在不是很主流,主流的方式是将计算得出的加密串直接改在uri的路径里。比如下面将要提到的例子。要求其实和早先那个squid防盗链的一模一样,就是改成用apache来跑。

Test.pm内容如下:

package Test;
use strict;
use warnings;
use Socket qw(inet_aton);
use POSIX qw(difftime mktime);
use Digest::MD5 qw(md5_hex);
use Apache2::RequestRec ();
use Apache2::Connection ();
use Apache2::RequestUtil ();
use Apache2::ServerUtil ();
use Apache2::Log ();
use Apache2::Request ();
use Apache2::Const qw(DECLINED FORBIDDEN);
sub handler {
    my $r = shift;
    my $s = Apache2::ServerUtil->server;
    my $secret = $r->dir_config('Secret') || '';
    my $uri = $r->uri() || '';
    my $expire = 2 * 3600;
    if ($uri =~ m#^/(d{4})(d{2})(d{2})(d{2})(d{2})/(w{32})(/S+.mp3)$#oi) {
        my ($year, $mon, $mday, $hour, $min, $md5, $path) = ($1, $2, $3, $4, $5, $6, $7);
        my $str = md5_hex($secret . $year . $mon . $mday . $hour . $min . $path);
        my $reqtime = mktime(00, $min, $hour, $mday, $mon - 1, $year - 1900);
        my $now = time;
        if ( $now - $reqtime < $expire){
            if ($str eq $md5) {
                $r->uri("$path");
                return Apache2::Const::DECLINED;
            }
        }
    }
}
1;

然后在httpd.conf中加上如下配置:

PerlPostConfigRequire /home/apache2/perl/start.pl
SetHandler modperl
PerlTransHandler Test
PerlSetVar Secret abcdef

这里需要注意几点,根据modperl的处理流程,修改uri的时候,handler还没有走到对文件进行寻址,所以无法区分文件路径等信息,故而 PerlTransHandler 配置不能在 <Directory><Location> 里面。

而在试用一里,核对strings是用的 PerlAccessHandler,当时已经确认了uri的文件路径,故而可以在 <Location> 里。

另,上面的pm,对错误访问返回的是404,如果需要403,return FORBIDDEN 就可以了。

如果想同时根据referer来防盗链,可能要在 PerlHeaderParserHandler 阶段在进行一次判定了,这个还在研究,不知道怎么取request-header的信息……