Thinkphp5.0 之APP开发接口非对称加密(RSA)加密方法(完整实例)

PHP 2173浏览 评论

在平时我们写接口的时候,常常会想如何写一个安全可靠的接口,因此我们会对比较核心或者敏感的参数进行加密,这样可以很好的防止在中途被不法分子拦截修改参数造成不可避免的损失!下面我们讲一下如何利用非对称加密(Rsa)对接口进行加密解密!

| 流程图,大致是这样吧


image.png

| 封装Rsa,生成密钥对,加密,解密类(环境要配置openssl,这个就不多讲了)

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Tangyijun <251784425@qq.com>
// +----------------------------------------------------------------------
// | ras加密类
// +----------------------------------------------------------------------
namespace app\service;
use app\models\Models;
use think\Config;

class Rsa{
    /**
     * 生成密钥
     * @param string $equipment
     * @throws \Exception
     * @return string $publicKey
     */
    public function generateKey($equipment){
        if(!$equipment){
            throw new \LogicException('生成密钥参数错误',1005);
        }
        $rsa = new Models('rsa_key');
        $publicKey = $rsa->getValue(['equipment' => $equipment],'public_key'); #有公钥则直接返回
        if(!$publicKey){
            $key = openssl_pkey_new(['private_key_bits' => 2048]); #加密串长度,此处必须int型
            if(!$key){
                throw new \LogicException('生成密钥失败',1006);
            }
            openssl_pkey_export($key, $privateKey);   #提取私钥
            $arrKey = openssl_pkey_get_details($key); #生成公钥
            $publicKey = $arrKey["key"];
            $data = ['equipment'=>$equipment,'public_key'=>$publicKey,'private_key'=>$privateKey,'create_time'=>time()];
            $bool = $rsa->_add($data);    #数据插入数据库
            if(!$bool){
                throw new \LogicException(Config::get('error_table.error')['msg'],Config::get('error_table.error')['code']);
            }
        }
        return $publicKey;
    }

    /**
     * 数据解密
     * @param string $equipment
     * @param json string $encrypt
     * @throws \Exception
     * @return array $decrypted
     */
    public function decrypt($equipment,$encrypt){
        if(!$equipment || !$encrypt){
            throw new \LogicException('解密参数错误',1007);
        }
        $rsa = new Models('rsa_key');
        //根据设备号查询私钥
        $privateKey = $rsa->_find(['equipment' => $equipment],'private_key'); #获取私钥
        if(!$privateKey){
            throw new \LogicException('获取私钥失败',1008);
        }
        //利用私钥进行解密操作
        openssl_private_decrypt(base64_decode($encrypt), $decrypted, $privateKey['private_key']); #私钥解密
        if(!$decrypted){
            throw new \LogicException('解密失败',1009);
        }
        //返回解密后的数据
        return (array)json_decode($decrypted);
    }

    /**
     * 数据加密
     * @param $publicKey
     * @param $data
     * @return string
     */
    public function encrypt($publicKey,$data){
        openssl_public_encrypt(json_encode($data), $encrypted, $publicKey);
        return base64_encode($encrypted);
    }
}

说明:由于是做app,因此每一台设备都有一个唯一的设备号,我们根据设备号来生成密钥对,这里我们不保存pem格式,每个用户都存文件格式,文件比较多维护起来比较麻烦,因此直接存储在数据中了!

表结构为:

image.png

| 格式化数据的函数(后面我们需要)

/**
 * ajax请求返回成功信息
 * @param  string $data
 * @return array
 */
public static function formatSuccessResult($data = null){
    return json(['code' => Config::get('error_table.success')['code'],'msg' => Config::get('error_table.success')['msg'],'data' => $data]);
}
/**
 * ajax请求返回错误信息
 * @param int $code
 * @param string $errorMsg
 * @param string $data
 * @return array
 */
public static function formatResult($code, $errorMsg, $data = null){
    return json(['code' => $code,'msg' => $errorMsg,'data'=>$data]);
}

| 利用设备号获取公钥(调用上面的generateKey()方法)


/**
 * @return string|void
 * 获得公钥
 */
public function rsaKey(){
    try{
        $rsa = new Rsa();//实例化rsa类
        $param =  input('param.'); 
        if(empty($param['equipment'])){
            throw new \LogicException('参数错误',10001);
        }
        $public_key = $rsa->generateKey($param['equipment']); //生成公钥
    }catch(\Exception $e){
        return self::formatResult($e->getCode(),$e->getMessage());
    }
    //返回格式化数据,该方法我放在下面吧
    return self::formatSuccessResult(['public_key'=>$public_key]);
}


| 移动设备使用公钥对参数加密(这里我们用php模拟加密)

public function encrypt(){
    $param = input('param.');
    $rsa = new Rsa();
    $rsa_key = new Models('rsa_key');
    $one = $rsa_key->_find(['equipment'=>$param['equipment']],'public_key'); //根据设备号查询出公钥
    if(empty($one)){
        echo '请先生成公钥';exit;
    }
    unset($param['equipment']);
    $encrypt = $rsa->encrypt($one['public_key'],$param);
   return self::formatSuccessResult($encrypt);
}


来个图片说明一下,利用postman可以测试一下,返回如下结果


image.png


| 得到了加密串,这个时候我们调用Rsa类的解密方法进行解密

public function productList(){
    try{
        if(!Request::instance()->isPost()){
            throw new \LogicException('请求方式错误',100020); //验证请求方式
        }
        $param = input('param.'); //接收参数
        $rsa = new Rsa(); //实例化加密类
        $data = $rsa->decrypt($param['equipment'],$param['sign']);           //数据解密
        self::parameterCheck('token|page|type',$data);                         //加密参数验证
        
    }catch(\Exception $e){
        return self::formatResult($e->getCode(),$e->getMessage());
    }
    dump($data);
    
}


结果说明来一张图:

image.png


讲了这么多不知道大家有没有学会呢!如果有什么不懂的大家可以留言!非常感谢


本文连接:http://www.phpbloger.com/article/21 文章都为原创,转载请注明出处!

相关文章