一个简单的API授权流程

实际项目中我们经常会碰到给第三方开放我们项目API的这种场景。这时我们要保证api的安全,参考腾讯广点通的api调用。现将php的代码实现整理这此。

我们的算法很简单:

  1. 把调用方自己的appid,secret_key,当前的时间戳time连接起来用sha1方法生成一个sign
  2. 把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) {
  }
}
*/

评论