通过 md5 加密后的字符串本身不具有效期限,而且值是恒定的。想要每次获取的值不一样而且能通过验证,就需要时间戳或随机数的参与,详见代码和注释:
/**
* Encode
* @param string $key 固定值,不传输
* @param string $str 参与加密的字符串, 自定义
* @return string
*/
function hashEncode($key, $str = '')
{
$time = strtotime(date('Y-m-d H:i', time()));
// 位数只能到 16, 超过后字母可能超过 f
$posStr = mt_rand(0, 1) . mt_rand(1, 6); // ASCII 65 - 90 A-Z, 参考 ASCII表
$hashPos = intval($posStr); // 截取的起始位置
$hash = md5($key . $str);
$hashCode = substr($hash, $hashPos, 4);
$signCode = md5($hash .$time. $hashCode);
$ascType = $hashPos > 10 ? 54 : 47; // 使用 asc 的字母或数字的 asc 数值, 数字的起始值为 48, 字母为 65, 避开中间的特殊字符
$sign = substr($signCode, 0, $hashPos). $hashCode .substr($signCode, $hashPos, -6). strtolower(chr( $hashPos + $ascType )) . substr($signCode, -1);
return $sign;
}
/**
* 验证
* @param string $sign 加密后的字符串
* @param string $key 固定值,不传输
* @param string $str 参与加密的字符串, 自定义 须与hashEncode中的一致
* @return boolean
*/
function hashVerify($sign, $key, $str = '')
{
$time = strtotime(date('Y-m-d H:i', time()));
$suffix = substr($sign, -2, 1);
$ascCode = ord(strtoupper($suffix));
$ascType = $ascCode > 64 ? 54 : 47;
$hashPos = $ascCode - $ascType;
$hash = md5($key . $str);
$hashCode = substr($sign, $hashPos, 4);
//$signCode = md5($hash . $time . $hashCode);
//$code = substr($signCode, 0, $hashPos) . $hashCode . substr($signCode, $hashPos, -6) . $suffix . substr($signCode, -1);
//避免时间增长, 有效期 60分钟
$expires = 60;
for($i = 0; $i < $expires; $i++){
$signCode = md5($hash .$time. $hashCode);
$code = substr($signCode, 0, $hashPos) . $hashCode . substr($signCode, $hashPos, -6) . $suffix . substr($signCode, -1);
if($sign == $code){
return true;
}
$time -= 60; //每次向前推60秒即1分钟
}
return false;
}
示用:
//client
$key = 'test';
$sign = hashEncode($key);
//server
$key = 'test';
$sign = isset($_GET['sign']) ? addslashes($_GET['sign']) : '';
$verify = hashVerify($sign, $key);
var_dump($verify);