过滤器

默认可用的MD5签名

基于很多同学对接口签名验证比较陌生,PhalApi提供了一个基本版的接口验证服务。主要是基于md5进行的签名生成,这个只能作为一般性的参考。大家可以在此基础上进行调整延伸。

默认情况下,在./config/di.php文件中去掉注释便可开启此接口验证,即:

  1. // 签名验证服务
  2. $di->filter = new \PhalApi\Filter\SimpleMD5Filter();

其验签的算法如下(如注释所示):

  1. 1、排除签名参数(默认是sign
  2. 2、将剩下的全部参数,按参数名字进行字典排序
  3. 3、将排序好的参数,全部用字符串拼接起来
  4. 4、进行md5运算

以下面的示例参数为例,即:

  1. 1、排除签名参数(默认是sign
  2. ?service=Examples_CURD.Get&id=1
  3. 2、将剩下的全部参数,按参数名字进行字典排序
  4. id=1
  5. service=Examples_CURD.Get
  6. 3、将排序好的参数,全部用字符串拼接起来
  7. "1Examples_CURD.Get" = "1" + "Examples_CURD.Get"
  8. 4、进行md5运算
  9. sign = 3ba5f5f03a90b2a648f5dd1df7387e26 = md5("1Examples_CURD.Get")
  10. 5、请求时,加上签名参数
  11. ?service=Examples_CURD.Get&id=1&sign=3ba5f5f03a90b2a648f5dd1df7387e26

下面是两个调用示例,错误请求下(即签名失败):

  1. http://dev.phalapi.net/?service=Examples_CURD.Get&id=1&sign=xxx
  2. 返回:
  3. {
  4. "ret": 406,
  5. "data": [],
  6. "msg": "非法请求:签名错误"
  7. }
温馨提示:签名错误情况下,可以查看日记获得正确的sign,如:2017-07-22 12:02:18|DEBUG|Wrong Sign|{"needSign":"3ba5f5f03a90b2a648f5dd1df7387e26"}

正常请求下(带sign签名):

  1. http://dev.phalapi.net/?service=Examples_CURD.Get&id=1&sign=3ba5f5f03a90b2a648f5dd1df7387e26

如果不想使用sign作为关键的签名参数,可以在注册时指定,如使用缩写s:

  1. $di->filter = new \PhalApi\Filter\SimpleMD5Filter('s');

白名单配置

对于不需要进行签名验证的接口服务,可以使用白名单配置,通过框架自身实现对指定配置的接口服务排除。即调用的接口服务,如果配置了白名单,则不调用过滤器。

接口服务白名单配置是:app.service_whitelist,即配置文件./config/app.php里面的service_whitelist配置,其默认值是:

  1. 'service_whitelist' => array(
  2. 'Site.Index',
  3. ),

如源代码里的注释所示,配置的格式有以下四种。

类型 配置格式 匹配规则 示例及说明
全部 . 匹配全部接口服务(慎用!) 如果配置了此规则,即全部的接口服务都不触发过滤器。
方法通配 Site.* 匹配某个类的任何方法 即App\Api\Site接口类的全部方法
类通配 *.Index 匹配全部接口类的某个方法 即全部接口类的Index方法
具体匹配 Site.Index 匹配指定某个接口服务 即App\Api\Site::Index()

如果有多个生效的规则,按短路判断原则,即有任何一个白名单规则匹配后就跳过验证,不触发过滤器。

以下是更多的示例:

  1. 'service_whitelist' => array(
  2. '*.Index', // 全部的Index方法
  3. 'Test.*', // Api_Test的全部方法
  4. 'User.GetBaseInfo', // Api_User::GetBaseInfo()方法
  5. ),

配置好上面的白名单后,以下这些接口服务全部不会触发过滤器:

  1. // 全部的Index方法
  2. ?service=Site.Index
  3. ?service=User.Index
  4. // Api_Test的全部方法
  5. ?service=Test.DoSth
  6. ?service=Test.Hello
  7. ?service=Test.GOGOGO
  8. // Api_User::GetBaseInfo()方法
  9. ?service=User.GetBaseInfo

命名空间白名单独立配置

如果需要为不同的命名空间独立配置白名单,只需要简单加多一层配置即可,即单独配置的路径是:

  1. app.service_whitelist.{命名空间}

对应的配置示例是:

  1. 'service_whitelist' => array(
  2. 'Site.Index',
  3. // 以命名空间名称为key
  4. 'App' => array(
  5. // 在这里,单独配置……
  6. ),
  7. ),

更好地建议

通常关于接口签名这块,我们还需要:

  • 1、为不同的接入方定义不同的密钥和私钥;
  • 2、如果业务需要,为各个接口、各个接入方分配调用权限;
  • 3、统一签名参数的规则,可以配置在./config/app.php中的,如上面的签名需要的参数,我们可以追加统一的参数规则:
    /**
    • 应用接口层的统一参数
      */
      'apiCommonRules' => array(
      'signature' => array('name' => 'signature', 'require' => true),
      'timestamp' => array('name' => 'timestamp', 'require' => true),
      'nonce' => array('name' => 'nonce', 'require' => true),
      ),

扩展:实现你的签名方式

如果我们需要实现签名验证,只需要简单的两步即可:

  • 1、实现过滤器接口 PhalApi\Filter::check()
  • 2、注册过滤器服务 PhalApi\DI()->filter
    下面以大家熟悉的 微信验签 为例,进行示例说明。

实现过滤器接口

通常我们约定返回ret = 402表示验证失败,所以当签名失败时,我们可以返回ret = 402以告知客户端签名不对。根据微信的检验signature的PHP示例代码,我们可以快速实现自定义签名规则,如:

  1. // 文件 ./src/app/Common/SignFilter.php
  2. <?php
  3. namespace App\Common;
  4. use PhalApi\Filter;
  5. use PhalApi\Exception\BadRequestException;
  6. class SignFilter implements Filter
  7. {
  8. public function check()
  9. {
  10. $signature = \PhalApi\DI()->request->get('signature');
  11. $timestamp = \PhalApi\DI()->request->get('timestamp');
  12. $nonce = \PhalApi\DI()->request->get('nonce');
  13. $token = 'Your Token Here ...';
  14. $tmpArr = array($token, $timestamp, $nonce);
  15. sort($tmpArr, SORT_STRING);
  16. $tmpStr = implode( $tmpArr );
  17. $tmpStr = sha1( $tmpStr );
  18. if ($tmpStr != $signature) {
  19. throw new BadRequestException('wrong sign', 1);
  20. }
  21. }
  22. }

注册过滤器服务

随后,我们只需要再简单地注册一下过滤器服务即可,在./config/di.php文件最后追加:

  1. // 签名验证服务
  2. $di->filter = new App\Common\SignFilter();

原文: http://docs.phalapi.net/#/v2.0/filter