基於時間戳防盜鏈的功能其實每家的CDN都是支持的。主要是經過使用約定的加密字符串來對具備訪問有效期的資源連接進行一些加密計算的到一個sign
值,而後訪問外鏈裏面帶上這個sign
和截止時間戳去訪問CDN的節點,CDN的節點會用一樣的算法來計算訪問連接是否合法,若是不合法則返回403 Forbidden
,不然返回所要訪問的資源。java
算法說明golang
基於時間戳的防盜鏈是經過對時間有關的字符串進行簽名,將時間,簽名經過必定的方式傳遞給CDN服務器做爲斷定依據,CDN邊緣節點依據約定的算法判斷來訪的URL是否有訪問權限。算法
若是經過,執行下一步;若是不經過,響應 HTTP 狀態碼 403。若是同時配置了Referer方式防盜鏈,UserAgent防盜鏈,時間戳防盜鏈,那麼若是有其中一項沒有經過,那麼即響應403。apache
簽名參數服務器
參數 | 描述 |
---|---|
T | URL過時的時間,把Unix以秒爲單位的時間戳,用16進制的小寫字母形式表示。好比 2016-06-30 22:57:42 +0800 CST 對應的時間戳是 1467298662 ,表示爲16進制就是 57753366 。 |
key | 和CDN約定好的加密字符串 |
path | 訪問資源外鏈的PATH部分,好比若是訪問的外鏈是http://if-pbl.qiniudn.com/golang.png?v=1 那麼其中PATH部分就是/golang.png |
簽名算法加密
待簽名的原始字符串 s=key+path+T
url
簽名方式 sign=md5(s).to_lower()
,其中to_lower()
表示生成的md5字符串用小寫字母表示.net
簽名參數傳遞方式code
例如原始訪問外鏈是:http://if-pbl.qiniudn.com/golang.png?v=1
最終造成的訪問外鏈是:http://if-pbl.qiniudn.com/golang.png?v=1&sign=xxxx&t=xxxx
orm
其中xxxx
對應各自的值。
算法參考實現
package com.qiniulab.cdn; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Hex; public class CdnAntiLeech { /** * 生成資源基於CDN時間戳防盜鏈的訪問外鏈 * * @param 資源原始外鏈 * @param 結果資源的有效期,單位秒 * @throws MalformedURLException * @throws UnsupportedEncodingException * @throws NoSuchAlgorithmException */ public static String getAntiLeechAccessUrlBasedOnTimestamp(String url, String encryptKey, int durationInSeconds) throws MalformedURLException, UnsupportedEncodingException, NoSuchAlgorithmException { URL urlObj = new URL(url); String path = urlObj.getPath(); long timestampNow = System.currentTimeMillis() / 1000 + durationInSeconds; String expireHex = Long.toHexString(timestampNow); String toSignStr = String.format("%s%s%s", encryptKey, path, expireHex); String signedStr = md5ToLower(toSignStr); String signedUrl = null; if (urlObj.getQuery() != null) { signedUrl = String.format("%s&sign=%s&t=%s", url, signedStr, expireHex); } else { signedUrl = String.format("%s?sign=%s&t=%s", url, signedStr, expireHex); } return signedUrl; } private static String md5ToLower(String src) throws UnsupportedEncodingException, NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(src.getBytes("utf-8")); byte[] md5Bytes = digest.digest(); return Hex.encodeHexString(md5Bytes); } }
使用方式
// cdn 配置的基於時間戳防盜鏈的加密字符串,cdn 配置完成後會獲得 String encryptKey = ""; // 待加密連接 String fileKey = "xxxx.pdf"; String encodedFileKey; try { // 考慮到文件名稱會有中文,因此須要作urlencode encodedFileKey = URLEncoder.encode(fileKey, "utf-8"); String urlToSign = String.format("http://img.abc.com/%s", encodedFileKey); // 有效期 int duration = 3600; String signedUrl = CdnAntiLeech.getAntiLeechAccessUrlBasedOnTimestamp(urlToSign, encryptKey, duration); System.out.println(signedUrl); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }