• thinkphp6: 给接口api做签名验证(php 8.1.1 / thinkphp v6.0.10LTS )


    一,创建middelware和controller

    liuhongdi@lhdpc:/data/php/admapi$ php think make:middleware CheckSign
    Middleware:app\middleware\CheckSign created successfully.
    liuhongdi@lhdpc:/data/php/admapi$ php think make:controller Pay
    Controller:app\controller\Pay created successfully.

    说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

             对应的源码可以访问这里获取: https://github.com/liuhongdi/
             或: https://gitee.com/liuhongdi

    说明:作者:刘宏缔 邮箱: 371125307@qq.com

    二,编写php代码:

    1,middleware/CheckSign.php
     
    <?php
    declare (strict_types = 1);
     
    namespace app\middleware;
     
    use app\result\Result;
    use app\lib\util\sign;
     
    class CheckSign
    {
        //第三方对接口的访问不需要检查
        private $notCheck = ["/pay/alipayreturn","/pay/alipayrotify"];
        /**
         * 处理请求
         *
         * @param \think\Request $request
         * @param \Closure       $next
         * @return Response
         */
        public function handle($request, \Closure $next)
        {
            $uri = $request->server("REQUEST_URI");
            //如果是不需检查的uri
            if (in_array($uri ,$this->notCheck)) {
                return $next($request);
            } else {
                $sign = new sign();
                $param = $request->request();
                unset($param['s']);
                //检查参数的sign
                $res = $sign->verifySign($param);
                if ($res['code'] == 0){
                    return $next($request);
                } else {
                    return Result::Error(1,$res['msg']);
                }
            }
        }
    }
    2,lib/util/sign.php
    <?php
    namespace app\lib\util;
     
    class sign {
        private $appId = "lc20220118";
        private $appKey = "u665698fzur5e2t85tyu142";
     
        //创建sign
        public function makeSign($data) {
            ksort($data);
            $string = $this->toUrlParams($data);
            $string = $string . "&key=" . $this->appKey;
            $string = md5($string);
            $result = strtolower($string);
            return $result;
        }
     
        //检验sign是否正确
        public function verifySign($data) {
            //check sign
           if (!isset($data['sign']) || !$data['sign']) {
              return ['code'=>1,'msg'=>'发送的数据签名不存在'];
           }
     
            //check sign
            if (!isset($data['appid']) || !$data['appid']) {
                return ['code'=>1,'msg'=>'发送的应用参数1不存在'];
            }
            if ($data['appid'] != $this->appId) {
                return ['code'=>1,'msg'=>'发送的应用参数1错误'];
            }
     
            //check sign
            if (!isset($data['nonce']) || !$data['nonce']) {
                return ['code'=>1,'msg'=>'发送的应用参数2不存在'];
            }
     
           //check timestamp
           if (!isset($data['timestamp']) || !$data['timestamp']) {
              return ['code'=>1,'msg'=>'发送的数据参数不合法'];
           }
     
           // 验证请求, 10分钟失效
           if (time() - $data['timestamp'] > 600) {
              return ['code'=>1,'msg'=>'验证超时, 请重新发送请求'];
           }
     
           $clientSign = $data['sign'];
            unset($data['sign']);
           $serverSign = $this->makeSign($data);
           if ($clientSign == $serverSign) {
             return ['code'=>0,'msg'=>'验证通过'];
           } else {
             return ['code'=>1,'msg'=>'请求不合法'];
           }
        }
     
        //生成url字符串
        private function toUrlParams($values){
            $buff = "";
            foreach ($values as $k => $v)
            {
                //&& $v != ""
                if($k != "sign" && !is_array($v)){
                    $buff .= $k . "=" . $v . "&";
                }
            }
            $buff = trim($buff, "&");
            return $buff;
        }
    }
    3,controller/Pay.php
    <?php
    declare (strict_types = 1);
     
    namespace app\controller;
     
    use think\Request;
     
    class Pay
    {
        /**
         * return
         *
         * @return \think\Response
         */
        public function alipayReturn()
        {
            echo "alipayReturn";
            exit;
        }
     
        /**
         * notify
         *
         * @return \think\Response
         */
        public function alipayNotify()
        {
            echo "alipayNotify";
            exit;
        }
    }

    三,编写vue代码:

    说明:vue代码仅供演示,因为js中无法安全保存appkey,
               所以web/wap等不需要做验证:
    <template>
      <div style="text-align: left;">
        id:{{article.id}}<br/>
        title:{{article.title}}<br/>
        content:{{article.content}}<br/>
      </div>
    </template>
     
    <script>
    import {apiArticleOne} from "../api/api";
    import {ElMessage} from "element-plus";
    import {ref} from "vue";
    import md5 from 'js-md5';
    export default {
      name: "Article",
      setup() {
     
        const article = ref([]);
        //得到sign
        const getSign = (param,key) => {
          let str = "";
          for(var idx in param) {
             let one = idx+"="+param[idx];
             if (str == "") {
                 str = one;
             } else {
                 str = str+"&"+one;
             }
          }
          str = str+"&key="+key;
          //console.log("before md5:"+str);
          let md5str = md5(str);
          return md5str;
        }
     
        //得到参数对象
        const getParam = (id) => {
           let appid = "lc20220118";
           let appkey = "u665698fzur5e2t85tyu1421";
           let timestamp = parseInt((new Date()).getTime()/1000);
           let nonce = Math.floor(Math.random()*8999)+1000;
     
           let param = {
               appid:appid,
               id:id,
               nonce:nonce,
               timestamp:timestamp,
           }
          let sign = getSign(param,appkey);
          param.sign = sign;
          return param;
        }
     
        //查询得到一篇文章的内容:
        const getArticle = () => {
          var param = getParam(1);
          apiArticleOne(param).then(res => {
            //成功
            if (res.code == 0) {
              article.value = res.data;
            } else {
              ElMessage.error("获取表单令牌失败:"+res.msg);
            }
          }).catch((error) => {
            console.log(error)
          })
        }
        getArticle();
        return {
          article,
        }
      }
    }
    </script>
     
    <style scoped>
    </style>

    四,测试效果:

    1,成功返回数据时:
    2,appkey不正确时:
    3,直接访问接口时报错:
    4,访问不做check的接口:

    五,查看php和thinkphp的版本:

    php:
    liuhongdi@lhdpc:/data/php/admapi$ php --version
    PHP 8.1.1 (cli) (built: Dec 20 2021 16:12:16) (NTS)
    Copyright (c) The PHP Group
    Zend Engine v4.1.1, Copyright (c) Zend Technologies
        with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies 
    thinkphp:
    liuhongdi@lhdpc:/var/www/html$ cd /data/php/admapi/
    liuhongdi@lhdpc:/data/php/admapi$ php think version
    v6.0.10LTS 
  • 相关阅读:
    介绍一些好用的ArcToolbox工具(默认ArcGIS没有)
    我的技术博客开始啦
    Qualcomm和MTK两个android平台比较
    kali安装nessus
    ImportError: cannot import name pxssh
    python 批量下载文件
    GitHub如何修改用户名
    Sass中使用@each循环
    PhpStorm 实用快捷键
    GitHub如何删除一个repository(仓库)
  • 原文地址:https://www.cnblogs.com/architectforest/p/15832850.html
Copyright © 2020-2023  润新知