QQ空間常常見到這種日誌,正文有一張圖片,在好友動態列表查看的時候能夠顯示本身(不是發日誌的好友,是你本身)的頭像和QQ號碼、暱稱。正文的文字稱,這是「本年度最給力神奇魔力日誌(轉載會看到你最熟悉的身影)」,而且在文章底部附加了一個發廣告的QQ號碼。php
一看就知道是檢測referer的把戲。早在幾年前BBS還在流行的時候,不少人設置的簽名圖具備天氣預報、客戶端信息(瀏覽器、操做系統、IP所 在地之類)、隨機笑話、倒計時等五花八門的功能。這都得益於服務器端腳本的圖像處理功能。而客戶端的檢測則是基於HTTP請求中的UserAgent和 Referer等信息。html
可是印象中QQ空間爲了防止referer潛在的安全問題和防止圖片被防盜鏈瞎了很大功夫,凡是發表到QQ空間的日誌,正文都會把引用到的全部第三方圖片資源緩存到騰訊的雲端上。因此直接在日誌正文中引用的圖片,是不會提交REFER到咱們的服務器腳本上的。json
文中特別稱,「請轉載後用電腦進入我的中心看」。爲何要特別說明是「我的中心」呢?我刷新了很久的動態,終於看到了圖片所說的效果。頁面生成的DOM代碼爲:後端
1
2
3
|
onload="QZFL.media.reduceImage(0,400,300,{trueSrc:'http:\/\/qq.sennvwu.com\/qzone\/do.php',callback:function(img,type,ew,eh,o){var
_h
=
Math
.floor(o.oh/o.k),
_w
=
Math
.floor(o.ow/o.k);if(_w<=ew && _h>=eh){var p=img.parentNode;p.style.width=_w+'px';p.style.height=_h+'px';}}})"
width="400">
|
原來QQ空間仍是會顯示源地址的圖片的,僅限於在「我的中心」。這時候請求圖片附帶的HTTP_REFERER的值爲瀏覽器
http://user.qzone.qq.com/123456789/infocenter緩存
號碼就是這樣提取到的。若是REFERER不知足條件,這個php將在header中發送Location跳轉到同一目錄下的no.png。安全
那麼後臺是如何取到QQ頭像、暱稱等信息的呢?我Google到了一個騰訊的WebService接口:服務器
http://base.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=123456789函數
不須要任何憑證信息便可獲取uins指定的QQ號碼的頭像、暱稱信息,返回的格式爲JSON。另外上面的圖片還有一個顯示地理位置和ISP的功能,這個就比較常見了。我找到了一個比較好用的接口,來自TB:字體
格式一樣也是JSON。
接下來實現這個效果就比較簡單了,經過REFERER檢測用戶的QQ號碼,而後在後臺下載頭像、暱稱等信息,用GD函數繪製上圖片,返回客戶端。
我也折騰了一個‘神奇圖片「發到空間,竟然捉弄了一羣人。下面是php語言的實現代碼。爲了減小後端的流量,對下載的頭像作了緩存處理:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
|
<?php
error_reporting
(0);
ob_start();
header(
'Content-Type: image/png'
);
define(
'IMG_NO'
,
"no.png"
); #剛開始顯示的提示信息
define(
'IMG_BACKGROUND'
,
"background.png"
);
define(
'IMG_WIDTH'
, 400);
define(
'IMG_HEIGHT'
, 128);
define(
'FONT_NAME'
,
"AdobeHeitiStd-Regular.otf"
); #字體文件名
define(
'CACHE_PATH'
, rtrim(
realpath
(
"./cache"
),
'/'
).
'/'
); #緩存目錄
define(
'CACHE_EXPIRE'
, 60*60); #緩存時間,單位秒
#(!
is_dir
(CACHE_PATH) &&
is_writable
(CACHE_PATH)) ||
die
;
/*
$remote: 遠程URL
$local: 本地緩存路徑
$expire: 過時時間。爲-1時,永久不更新緩存
*/
function
load_from_cache(
$remote
,
$local
,
$expire
= CACHE_EXPIRE,
$as_path
= false) {
//過濾潛在的危險字符
$local
= preg_replace(
"/[.\/\\\?\*\'\"\|\:\<\>]/"
,
"_"
,
$local
);
$cache
= CACHE_PATH.
$local
;
//查找緩存
if
(
file_exists
(
$cache
) && (
$expire
= -1 ||
filemtime
(
$cache
) - time() <
$expire
))
return
$as_path
?
$cache
:
file_get_contents
(
$cache
);
//文件不存在或緩存過時,從新下載
$content
=
file_get_contents
(
$remote
);
file_put_contents
(
$cache
,
$content
);
return
$as_path
?
$cache
:
$content
;
}
/*
返回客戶端信息。
*/
function
client_info() {
$ip
= (
$_SERVER
[
"HTTP_VIA"
] &&
$_SERVER
[
"HTTP_X_FORWARDED_FOR"
] ?
$_SERVER
[
"HTTP_X_FORWARDED_FOR"
] :
$_SERVER
[
"REMOTE_ADDR"
]);
$info
=
explode
(
'"'
, load_from_cache(
$url
.
$ip
,
$ip
, -1));
$string
=
$info
[7].
$info
[23].
$info
[31].
$info
[47];
return
json_decode(
'"'
.
$string
.
'"'
);
}
$referer
=
$_SERVER
[
'HTTP_REFERER'
];
//$referer = "http://user.qzone.qq.com/123456789/infocenter";
$pattern
=
"/http:\/\/user.qzone.qq.com\/(\d+)\/infocenter/"
;
if
(preg_match(
$pattern
,
$referer
,
$matches
)) {
//獲取QQ號碼
$uin
=
$matches
[1];
$info
=
explode
(
'"'
, load_from_cache(
$avatar
=
$info
[3];
$nickname
= iconv(
"GBK"
,
"UTF-8//IGNORE"
,
$info
[5]);
$client
= client_info();
//重點來了,生成圖片
try
{
$im
= imagecreatefrompng(IMG_BACKGROUND);
//繪製頭像
$avatar_file
= load_from_cache(
$avatar
,
$uin
.
".jpg"
, 60*60*24, true);
$im_avatar
= imagecreatefromjpeg(
$avatar_file
);
imagecopymerge(
$im
,
$im_avatar
, 14, 14, 0, 0, 100, 100, 100);
imagedestroy(
$im_avatar
);
//繪製文字
$blue
= imagecolorallocate(
$im
, 0, 0x99, 0xFF);
$white
= imagecolorallocate(
$im
, 0xFF, 0xFF, 0xFF);
$texts
=
array
(
array
(12, 148, 40,
$white
,
$uin
),
array
(18, 125, 70,
$blue
,
$nickname
),
array
(16, 125, 100,
$blue
,
$client
)
);
foreach
(
$texts
as
$key
=>
$value
) {
imagettftext(
$im
,
$value
[0], 0,
$value
[1],
$value
[2],
$value
[3], FONT_NAME,
mb_convert_encoding(
$value
[4],
"html-entities"
,
"utf-8"
));
//解決亂碼問題
}
imagepng(
$im
);
imagedestroy(
$im
);
header(
"Content-Length: "
.ob_get_length());
ob_end_flush();
}
catch
(Exception
$e
) {
//die($e->getMessage());
$error
= true;
}
}
else
{
$error
= true;
}
if
(
$error
){
header(
'Content-Length: '
.
filesize
(IMG_NO));
echo
file_get_contents
(IMG_NO);
}
|
WORDPRESS把縮進都吞掉了,看上去很是不爽。