• 上海市食药监局对外与第三方网络订餐平台之间数据交换的实现


      最近工作中有个笑脸需求。关于上海市食药监局对于餐厅食品安全评级的。简单些就是,第三方外卖公司等,需要调用他们的接口,获取餐厅评级,在自家网站上展示出来。大概是下面过程:

    1)  提交第三方认证信息,取得Token;

    2)  取得Token成功后,加入到Authorization header进行正常的业务接口调用;

    3)  Token还在有效期内,继续上述步骤,如果Token已失效,回到步骤1。

    看起来是个挺简单的,发送自己的认证信息,调接口获取就ok。可是仔细看看他们的文档,提交的第三方认证信息需要一个加密过程。对php加密不是很了解,所以犯难了。

    先看看第三方认证信息吧。

    名称

    说明

    id

    第三方标识

    name

    第三方名称

    password

    密码

    website

    第三方网站地址

    这些都是公司相关认证信息。对以上信息需要进行如下加密:

    1)   用使用者信息,及随机生成的GUID组成的JSON字符串作明文

    例:

    {

    “id”:”xx”,

    “name”:”XX”,

    “website”:”www.XX.com”,

    “gkey”:”379043FD-7335-40F6-874D-9FCFC6F84B42”

    }

    2)   用AES方法加密上述明文

    AES加密时的参数选择,加密模式:CBC;填充模式:PKCS7。

    加密时用的IV与密钥是基于当前日期、密码及一些使用者信息生成。

    初始化向量(IV)通过如下方式得到:

    1. 使用者标识、使用者名称直接拼成字符串;
    2. 对上一步的字符串进行UTF8编码得到一个byte数组;
    3. byte数组如果大于16位,截取前16位作为IV;如果不满16位则循环填充至16位作为IV

    密钥由下面方法得到:

    1. 使用者密码、网站地址及当前日期的yyyyMMdd格式字符串直接拼成一个字符串;
    2. 对上一步的字符串进行UTF8编码得到一个byte数组;
    3. byte数组如果大于32位,截取前32位作为密钥;如果不满32位则循环填充至32位作为密钥

    3)   上一步加密得到的byte数组转成base64编码的字符串

    4)        字符串进行URL编码

    网上关于AES的加密代码很多,可没有接触过php的mcrypt加密的我来说,还是遇到了一些小坑。现在详细说下实现方式。

    第一步。是要加密的明文。没有用到过GUID的小伙伴,可能会困惑。其实只是一个随机字符串。生成方式如下:

        public function createGuid() {
            $charid = md5(uniqid(mt_rand(), true));
            $hyphen = chr(45);// "-"
            $uuid = substr($charid, 0, 8).$hyphen
            .substr($charid, 8, 4).$hyphen
            .substr($charid,12, 4).$hyphen
            .substr($charid,16, 4).$hyphen
            .substr($charid,20,12);
            return $uuid;
        }
    View Code

    第二步。关于这个byte数组让人很困惑,莫非要转成byte数组?但是在使用mcrypt加密的时候发现要求传入字符串,后来食药监局的技术人员耐心讲解道:加密的底层实现都是utf8的byte数组。所以只要传utf8的字符串就可以了,至于byte数组,应该是mcrypt内部实现的,不用管了。生成初始化向量和密匙的第三条里的填充用pcks7方式填充。关于pcks7填充,是怎么个填充,看代码,一目了然。

        function pkcs7padding($source){
            $source = trim($source);
            $block = mcrypt_get_block_size('rijndael-128', 'cbc');
            $pad = $block - (strlen($source) % $block);
            if ($pad <= $block) {
                $char = chr($pad);
                $source .= str_repeat($char, $pad);
            }
            return $source;
        }
    View Code

    到这里似乎该加密了。带着激动的心情发过去,不出意料的,不对。原来,这里所谓pcks7填充模式,不仅是填充初始化向量和密匙,加密的明文也要填充滴。

    下面是整体的代码:

    class AesCrypterClass {
      const URL="http://spzf.smda.gov.cn/";
      static $companyInfo = array('id'=>'xx',
        'name'=>'xx',
        'website'=>'www.xx.cn',
        'password'=>'xx'
      ); 
    
      private $algorithm;
      private $mode;
      public function __construct($algorithm = MCRYPT_RIJNDAEL_128,$mode = MCRYPT_MODE_CBC) {
        $this->algorithm = $algorithm;
        $this->mode = $mode;
      }
      /**
      *生成validate验证字符串
      *AES加密,加密模式:CBC;填充模式:PKCS7。
      * @since 2015-11-16
      * @param array $orig_data 参数说明
      * string id 第三方标识
      * string name 第三方名称
      * string password 密码 
      * string website 第三方网站地址
      * @return string 
      */
      public function createValidate() {
        $orig_data = self::$companyInfo;
        $iv = $this->getIvParameter(array('id'=>$orig_data['id'],'name'=>$orig_data['name']));
        $key = $this->getKey(array('password'=>$orig_data['password'],'website'=>$orig_data['website']));
        $guid = $this->createGuid();
        $data = array('id'=>$orig_data['id'],
        'name'=>$orig_data['name'],
        'website'=>$orig_data['website'],
        'gkey'=>$guid
        );
        $jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
        $jsonData = $this->pkcs7padding($jsonData);
        $encrypted = mcrypt_encrypt($this->algorithm, $key, $jsonData, $this->mode, $iv);
        $encryptText = urlencode(base64_encode($encrypted));
        return $encryptText;
      }
      /**
      *pkcs7填充
      * @since 2015-11-16
      */
      function pkcs7padding($source){
        $source = trim($source);
        $block = mcrypt_get_block_size('rijndael-128', 'cbc');
        $pad = $block - (strlen($source) % $block);
        if ($pad <= $block) {
          $char = chr($pad);
          $source .= str_repeat($char, $pad);
        }
        return $source;
      }
      /**
      *字符串转换为utf8编码方式
      * @since 2015-11-16
      */
      public function utf8str($str)
      { 
        $encode = mb_detect_encoding( $str, array('ASCII','UTF-8','GB2312','GBK'));
        if ( $encode !='UTF-8' ){
          $str = iconv($encode,'UTF-8',$str);
        }
        return $str;
      }
        /**
        *生成初始化向量IV
        * @since 2015-11-16
        * @param array $args 参数说明
        * string id 第三方标识
        * string name 第三方名称
        * @return string 
        */
        public function getIvParameter($args){
            $str = $args['id'].$args['name'];
            $utf8str = $this->utf8str($str);
            if(strlen($utf8str)<16){
                $iv = $this->pkcs7padding($utf8str, 16);
            }else{
                $iv = substr($utf8str, 0, 16);
            }
            return $iv;
        }
        /**
        *生成密匙
        * @since 2015-11-16
        * @param array $args 参数说明
        * string password 密码 
        * string website 第三方网站地址
        * @return string 
        */
        public function getKey($args){
            $str = $args['password'].$args['website'].date('Ymd');
            $utf8str = $this->utf8str($str);
            if(strlen($utf8str)<32){
            $key = $this->pkcs7padding($utf8str, 32);
            }else{
                $key = substr($utf8str, 0, 32);
            }
            return $key;
        }
        /**
        *生成随机数GUID
        * @since 2015-11-16
        */
        public function createGuid() {
            $charid = md5(uniqid(mt_rand(), true));
            $hyphen = chr(45);// "-"
            $uuid = substr($charid, 0, 8).$hyphen
            .substr($charid, 8, 4).$hyphen
            .substr($charid,12, 4).$hyphen
            .substr($charid,16, 4).$hyphen
            .substr($charid,20,12);
            return $uuid;
        }
        /**
        *获取Token
        * @since 2015-11-16
        * @param array $args 参数说明
        * string id 第三方标识 
        * string validate 验证字符串
        * @return string 
        */
        public function getToken($args){
            $url = self::URL.'api/user/authenticate?        id='.$args['id'].'&validate='.$args['validate'];
            $ch = curl_init ();
            curl_setopt ( $ch, CURLOPT_URL, $url);
            curl_setopt ( $ch, CURLOPT_HEADER, 0 );
            curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
            $return = curl_exec ( $ch );
            $httpCode= curl_getinfo($ch,CURLINFO_HTTP_CODE);
            //403错误可能由于8小时内重新拉取token导致,加reCheck参数重新获取
            if($httpCode=='403'){
                $url = $url.'&reCheck=1';
                curl_setopt ( $ch, CURLOPT_URL, $url);
                $return = curl_exec ( $ch );
                $httpCode= curl_getinfo($ch,CURLINFO_HTTP_CODE);
            }
            curl_close ( $ch ); 
            return array('httpCode'=>$httpCode,'token'=>$return);
        }
        /**
        *获取餐厅分级信息
        * @since 2015-11-16
        * @param array $args 参数说明
        * string id 餐厅许可证号 
        * string token 验证token
        * @return array
        * License string 许可证号
        * OrgName string 单位名称
        * CheckDate date 检查日期
        * CheckResult string 检查结果,为良好、一般、较差
        * Signage string 店招,会有无效或无意义的值,仅供参考
        * Address string 地址
        */
        public function getRestaurantGradeInfo($args){
            $url = self::URL.'api/supervise/'.$args['id'];
            $headerArr = array("HOST:{$args['host']}","Authorization:FY     {$args['token']}");
            $ch = curl_init ();
            curl_setopt ( $ch, CURLOPT_URL, $url);
            curl_setopt ( $ch, CURLOPT_HEADER, 0 );
            curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headerArr);
            curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
            $return = curl_exec ( $ch );
            $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
            curl_close ( $ch ); 
            return array('httpCode'=>$httpCode,'gradeItem'=>$return);
        }
    
    }                                                                                                         
    View Code
  • 相关阅读:
    基本二叉搜索树的第K小元素
    sklearn常见分类器(二分类模板)
    python图论包networks(最短路,最小生成树带包)
    PAT 甲级 1030 Travel Plan (30 分)(dijstra,较简单,但要注意是从0到n-1)
    PAT 甲级 1029 Median (25 分)(思维题,找两个队列的中位数,没想到)*
    Oracle 10g ORA-12154: TNS: could not resolve the connect identifier specified 问题解决! 我同事遇到的问题。 username/
    JavaScritpt的DOM初探之Node(一)
    怎样实现动态加入布局文件(避免 The specified child already has a parent的问题)
    Ubuntu 14.04下单节点Ceph安装(by quqi99)
    卡片游戏
  • 原文地址:https://www.cnblogs.com/tomorrowdemo/p/5001312.html
Copyright © 2020-2023  润新知