PHP 的签名认证
阅读原文时间:2021年04月22日阅读:1

这两天在做第三方的签名验证,在网上看到一篇帖子还不错,分享给大家。

我也是借鉴了这个思路,然后利用redis缓存的token,进行接口的验证

<?php 

/**
 * 签名认证算法
 * HMAC-SHA256加密方式
 * 登录认证加入access_token:access-token通过登录接口去获取,通过刷新接口去刷新,需要注意返回的过期时间,要在过期时间之前刷新重新获取access-token。目前约定所有接口都必须传access-token,也就是用户必须先登录才可以看到相关内容
 * 随机函数可换更好的方法-本实例随机函数比较简单,随机性不够
 * ApiSign类作为服务端,类以外的代码作为客户端示例代码
 */
class ApiSign 
{
    CONST DELAY_TIME = 2000;
    CONST ACCESS_TOCEN_PATH = './access_token';

    private $_config = [];
    protected $AppKey = 'voBVVQxfMxDmhuxV70';
    protected $AppSecret = 'QJF5P8qWFJakF9Ve89ZcIstHKbkt5fVA';
    protected $timeout = 300;
    protected $algo = 'sha256';
    public $loginCheck = false;

    public function __construct($config, $loginCheck)
    {
        $this->_config = $config;
        empty($this->AppKey) && $this->AppKey = $this->generateRandomString();
        empty($this->AppSecret) && $this->AppSecret = $this->generateRangeNum();
        $this->loginCheck = $loginCheck;
        if ($this->init()) {
            echo '<span style="color:red" id="sign_success">恭喜,签名认证成功</span><script type="text/javascript"></script>';
            echo <<<JS
                <script>
                    var colors = ['#ffff00', '#ff66ff', '#99cc33', '#66ff33', '#000000', '#FF83FA', '#CAE1FF'];
                    var tmp = 0;
                    var timer = setInterval(colorChanage, 1000);
                    function colorChanage()
                    {
                        if(tmp == colors.length) {
                            tmp = 0;
                        }
                        document.getElementById('sign_success').style.color = colors[tmp++];
                    }
                </script>
JS;
        }
    }

    protected function init()
    {
        if (!isset($this->_config['_key']) 
            || !isset($this->_config['_sign'])
            || !isset($this->_config['_time'])
            || !isset($this->_config['_nonce'])
            || strlen($this->_config['_nonce']) !== 32
            ||  !is_numeric($this->_config['_time'])) {
                $this->callback(['message' => '请求参数不全, 或参数不规范'], 'error');
            }

        if (!isset($this->_config['_time']) || $this->getIsTimeOut()) {
            $this->callback(['message' => '请求超时'], 'error');
        }

        // 客户端验证
        $requestSignature = $this->_config['_sign'];
        $requestSignature = str_replace(' ', '+', $requestSignature);
        if($this->_config['_key'] !== $this->AppKey) {
            $this->callback(['message' => '非法的app_key'], 'error');
        }

        $signature = $this->generateSign($this->_config);
        if ($requestSignature != $signature) {
            $this->callback(['message' => '签名错误'], 'error');
        }

          $accessToken = $this->getAccessToken();

          // 用户登录token验证
          if ($this->loginCheck && $accessToken != $this->_config['access_token']) {
            $this->callback(['message' => 'access_token错误'], 'error');
          }

        return true;
    }

    private function callback($json=[], $status='ok')
    {
        $config = [
            'ok' => [200, '操作成功'],
            'error' => [300, '操作失败'],
            'timeout' => [300, '操作超时'],
        ];
        $json['statusCode'] = $config[$status][0];
        !isset($json['message']) && $json['message'] = $config[$status][1];
        echo json_encode($json);
        exit;
    }

    private function getIsTimeOut()
    {
        if (abs($this->_config['_time'] - time()) > $this->timeout) {
            return true;
        }

        return false;
    }

   /**
     * 生成随便数
     */
    public  function generateRangeNum($length = 32, $isToLower = false)
    {
        $str = $this->generateRandomString($length);
        if ($isToLower) {
            $str = strtolower($str);
        }

        return $str;
    }

    private function generateRandomString($length = 10) { 
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
        $randomString = ''; 
        for ($i = 0; $i < $length; $i++) { 
            $randomString .= $characters[rand(0, strlen($characters) - 1)]; 
        } 

        return $randomString; 
    }

    protected function generateSign($params)
    {
        if (isset($params['_sign'])) {
            unset($params['_sign']);
        }

        if($this->loginCheck && isset($params['access_token'])) {
            unset($params['access_token']);
        }

        ksort($params);
        $str = '';
        foreach ($params as $key => $value) {
            $str .= $key . '=' . $value . '&';
        }

        $str = rtrim($str, '&');

        return hash_hmac($this->algo, $str, $this->AppSecret, false);
    }

    public  function getAccessToken()
    {

        if (!file_exists(self::ACCESS_TOCEN_PATH)) {
            $json = ['value' => $this->_config['access_token'], 'expires_in' => 7200, 'time' => time()];
            file_put_contents(self::ACCESS_TOCEN_PATH, json_encode($json));
            return $this->_config['access_token'];
        }

        $accessToken = json_decode(file_get_contents(self::ACCESS_TOCEN_PATH), true);
        if (time() - $accessToken['time'] > $accessToken['expires_in'] - self::DELAY_TIME) {
            $json = ['value' => $this->_config['access_token'], 'expires_in' => 7200, 'time' => time()];
            file_put_contents(self::ACCESS_TOCEN_PATH, json_encode($json));
            return $this->_config['access_token'];
        }    

        return $accessToken['value'];    

    }
}

/**
 * 生成随机数
 */
function generateRangeNum($length = 32, $isToLower = false)
{
    $str = generateRandomString($length);
    if ($isToLower) {
        $str = strtolower($str);
    }

    return $str;
}

function generateRandomString($length = 10) { 
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
    $randomString = ''; 
    for ($i = 0; $i < $length; $i++) { 
        $randomString .= $characters[rand(0, strlen($characters) - 1)]; 
    } 

    return $randomString; 
}

function generateSign($algo, $params, $AppSecret)
{
    if (isset($params['_sign'])) {
        unset($params['_sign']);
    }

    ksort($params);
    $str = '';
    foreach ($params as $key => $value) {
        $str .= $key . '=' . $value . '&';
    }

    $str = rtrim($str, '&');

    return hash_hmac($algo, $str, $AppSecret, false);
}

header("content-type:text/html;charset:utf-8");
$algo = 'sha256';
$AppKey = 'voBVVQxfMxDmhuxV70';
$AppSecret = 'QJF5P8qWFJakF9Ve89ZcIstHKbkt5fVA';
$nonce = generateRangeNum(32);

$loginCheck = false;
$params = [
    '_key' => $AppKey,
    '_time' => time(),
    '_nonce' => $nonce,
];
$sign = generateSign($algo, $params, $AppSecret);
$params['_sign'] = $sign;

$loginCheck && $params['access_token'] = generateRangeNum(16);
// echo '<pre>';print_r($params);
$apiSignModel = new ApiSign($params, $loginCheck);

原文链接:https://www.cnblogs.com/YangJieCheng/p/8515185.html