Created
September 24, 2015 04:01
-
-
Save crossai-2033/55797ef1e808948bd825 to your computer and use it in GitHub Desktop.
Api接口设计算法
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
当我们设计对外Api接口的时候,要考虑接口的安全性,防止被拦截或者被重放攻击等,所以接口的对外设计要有一套验证机制。 | |
接口的设计主要双方就是接口提供方以及接口使用方,所考虑的方案就是双向加密认证,双方使用一个统一的Token,然后用Token加上固定的混淆规则 | |
然后双方都使用同样的规则来产生一个认证sign来验证。 | |
<!-- Api验证函数 --> | |
<?php | |
class ApiCheck | |
{ | |
const MAX_TIME_DIFF = 600; | |
const SIGN_CUT_BGN = 3; | |
const SIGN_CUT_CNT = 7; | |
public static function validParamTime($time) | |
{ | |
$diff = abs($time - time()); | |
return $diff < self::MAX_TIME_DIFF ? true : false; | |
} | |
public static function validParamSign($sign, $token, $params) | |
{ | |
if (isset($params['sign'])) { | |
unset($params['sign']); | |
} | |
ksort($params); | |
$params_str = http_build_query($params); | |
$secret = md5(md5($params_str).$token); | |
$valid_sign = substr($secret, self::SIGN_CUT_BGN, self::SIGN_CUT_CNT); | |
return $sign == $valid_sign ? true : false; | |
} | |
} | |
// 调用接口时候先执行验证函数 | |
private function verifyCommonParams() | |
{ | |
if (false === ApiCheck::validParamTime($this->_request_params['t'])) { | |
throw new Exception(ErrnoKnowledge::E_DW2014_INVALID_PHONE, '时间参数不合法'); | |
} | |
$appKey = $this->_request_params['appKey']; | |
$appInfo = $this->getAppInfo($appKey); | |
$appSecret = $appInfo['AppSecret']; | |
$token = $this->getApiToken($appKey, $appSecret); // 313c9a8d5c0c03425e30bf9b713fc059 | |
if (false === ApiCheck::validParamSign($this->_request_params['sign'], $token, $this->_request_params)) { | |
throw new Exception(HErrnoKnowledge::E_DW2014_INVALID_PHONE, 'sign认证不正确'); | |
} | |
} | |
public function getApiToken($appKey, $appSecret) | |
{ | |
return md5('_app_'.$appKey.'_secret_'.$appSecret); | |
} | |
// 然后接口使用方的使用方法为 | |
// 保证唯一key,并按照统一规则生成Token | |
function getToken() | |
{ | |
$appKey = 'BaoMiTu'; | |
$appSecret = 'df16e791bf8da7284a5b6ed56197fa28'; | |
return md5('_app_'.$appKey.'_secret_'.$appSecret); | |
} | |
// API测试 | |
$REQUEST_TIMEOUT = 3; | |
$REQUEST_RETRY = 3; | |
$curl = CurlSvc::ins(); | |
$host = array('127.0.0.1'); | |
$host = array(); | |
// ------------------------> | |
// 按照统一规则生成API函数 | |
$params = array( //请求参数 | |
'appKey' => 'BaoMiTu', | |
't' => time(), //客户端时间和服务端时间的差值在一个允许的范围内 | |
'nonce' => substr(md5(microtime(true).rand(0, 999)), 0, 16), //随机串,防重放攻击 | |
); | |
$token = getToken(); //分配给客户端的token | |
ksort($params); //请求参数key按照字典升序排序 | |
$params_str = http_build_query($params); //拼成k1=v1&k2=v2 ...的形式 | |
$secret = md5(md5($params_str).$token); //md5后把token拼接到后面再次md5 | |
$sign = substr($secret, 3, 7); //从下标3开始取7个字符即为最终签名 | |
$time = time(); | |
$url = "http://xxxx/weixin/getAccessToken?fmt=json&$params_str&sign=$sign"; | |
echo $url; | |
echo "\r\n"; | |
$response = $curl->get($url, $REQUEST_TIMEOUT, $host, $REQUEST_RETRY); | |
$http_code = $curl->getHttpCode(); | |
$response = json_decode($response); | |
echo $response->data; | |
至此双方就形成了同一套加密规则,都使用同一个Token来进行了接口验证 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment