基于时间戳防盗链的功能其实每家的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(); }