实际项目中我们经常会碰到给第三方开放我们项目API的这种场景。这时我们要保证api的安全,参考腾讯广点通
的api调用。现将php的代码实现整理这此。
我们的算法很简单:
- 把调用方自己的appid,secret_key,当前的时间戳time连接起来用sha1方法生成一个sign
- 把appid,time,sign用英文逗号连接并用base64打包变成一个参数token
API的几个方法
api_functions.php
<?php
/**
* 生成token
* @param string $appid
* @param string $secret_key
* @param int $time
* @return string
*/
function generate_token($appid, $secret_key, $time)
{
$sign = sha1($appid . $secret_key . $time);
return base64_encode($appid . ',' . $time . ',' . $sign);
}
/**
* 解包token
* @param string $token
* @return array
*/
function unpack_token($token){
$params = base64_decode($token);
$params = explode(',', $params);
return [
'appid'=> isset($params[0]) ? $params[0] : '',
'time'=> isset($params[1]) ? $params[1] : '',
'sign'=> isset($params[2]) ? $params[2] : '',
];
}
/**
* @param $url
* @param array $data
* @param bool $is_post
* @param array $header
* @return mixed
*/
function sub_curl($url, $data = array(), $is_post = false, $header = array())
{
$ch = curl_init();
if (!$is_post && !empty($data)) {
$url = $url . '?' . http_build_query($data);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, $is_post);
if ($is_post) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
if (!empty($header)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
$info = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($code != 200){
echo_json($code, 'api调用出错'.$code);
}
curl_close($ch);
return $info;
}
/**
* @param int $code
* @param string $msg
* @param array $data
*/
function echo_json($code, $msg = '', $data = array()){
header('Content-type:application/json');
echo json_encode(array('ret' => $code, 'msg' => $msg, 'data' => $data));
exit;
}
API提供方
api.php
<?php
require 'api_functions.php';
//授权的使用方名单 现为演示方便直接使用数组
$users = [
'user_001'=>[
'appid'=>'user_001', //使用方对于提供方api的唯一id
'secret_key'=>'97bc847d4ea7dd9f035d41a657302f1c' //密钥 也唯一
],
'user_002'=>[
'appid'=>'user_002',
'secret_key'=>'c763b64a62186ae6831edd22063539c4'
],
'user_003'=>[
'appid'=>'user_003',
'secret_key'=>'51a683bea5e5c138fd0342fb70e03c65'
],
];
//检验签名
$token = isset($_GET['token']) ? trim($_GET['token']) : '';
if(empty($token)){
echo_json(1000, 'token missed');
}
//解包token
$token_params = unpack_token($token);
if(empty($token_params['sign'])){
echo_json(1001, 'sign error'); //签名为空或者错误
}
if(empty($token_params['appid'])){
echo_json(1002, 'appid error'); //appid为空或者错误
}
if(empty($token_params['time'])){
echo_json(1003, 'time error'); //time为空或者错误
}
if(abs($token_params['time'] - time()) > 10 * 60){ // api 调用时间限制左右浮动10分钟
echo_json(1004, 'time expired'); // 10 minutes
}
//用appid取用户
$user = isset($users[$token_params['appid']]) ? $users[$token_params['appid']] : [];
if(empty($user)){
echo_json(1005, 'appid not exists'); //调用方不存在
}
//使用调用方参数生成token
$create_token = generate_token($user['appid'], $user['secret_key'], $token_params['time']);
if($token !== $create_token){
echo_json(1006, 'token error'); //token错误
}
//到此 调用权限的验证就ok了
$api = isset($_GET['api']) ? trim($_GET['api']) : '';
//接下来你可以有其他对具体接口的验证...
//返回结果
echo_json(200, 'your request api '.$api. ' success!');
API调用方
use_api.php
<?php
require 'api_functions.php';
//假设使用方是 user_001 他拥有自己的appid和secret_key
$user = [
'appid' => 'user_001', //使用方对于提供方api的唯一id
'secret_key' => '97bc847d4ea7dd9f035d41a657302f1c' //密钥 也唯一
];
//生成token
$token = generate_token($user['appid'], $user['secret_key'], time());
//url换成你自己的接口url
$url = 'http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']);
$url .= '/api.php?api=user_info'; //调用user_info的接口
$url .= '&token=' . $token;
echo $url;
echo '<hr><pre>';
//请求接口
$res = sub_curl($url);
var_dump(json_decode($res, 1));
/* 调用成功结果
array(3) {
["ret"]=>
int(200)
["msg"]=>
string(35) "your request api user_info success!"
["data"]=>
array(0) {
}
}
*/
评论