Api.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php namespace App\Http\Controllers\Api;
  2. use App\Exceptions\Api\VerifySignException;
  3. use App\Exceptions\Api\LoginException;
  4. use App\Facades\Servers\Encrypts\AccessToken;
  5. use App\Facades\Servers\Encrypts\ApiSign;
  6. use App\Http\Controllers\Controller;
  7. use App\Models\Custom;
  8. /**
  9. * 接口控制器
  10. *
  11. * @author 刘相欣
  12. *
  13. * */
  14. class Api extends Controller{
  15. /**
  16. * 登录验证
  17. *
  18. * @return Int $authcode 登录信息
  19. *
  20. */
  21. public function checkLogin($authcode=''){
  22. // 默认接参
  23. $authcode = $authcode ? $authcode : request('authcode','');
  24. // 如果没有有登录信息
  25. if( !$authcode ) throw new LoginException("please_login");
  26. // 解码
  27. $decode = AccessToken::decode($authcode);
  28. // 错误返回
  29. if( isset($decode['error']) ) throw new LoginException("login_error");
  30. // 如果没有过期时间,过期处理
  31. if( empty($decode['exp']) ) throw new LoginException("login_exprie");
  32. // 如果过期,过期处理
  33. if( $decode['exp'] <= time() ) throw new LoginException("login_exprie");
  34. // 从缓存获取登录数据
  35. $memAuthcode = (new Custom())->getLoginAuthCodeByUid($decode['sub']);
  36. // 不存在登录信息,提示失败
  37. if( !$memAuthcode ) throw new LoginException("login_miss");
  38. // 如果是32位的话
  39. if( strlen($memAuthcode) == 32 ) $authcode = md5($authcode);
  40. // 如果存在登录数据,并且不是当前用户的缓存登录,提示失败
  41. if( $authcode != $memAuthcode ) throw new LoginException("login_miss");
  42. // 返回用户ID
  43. return $decode['sub'];
  44. }
  45. /**
  46. * 登录验证
  47. *
  48. * @return Int 用户ID
  49. *
  50. */
  51. public function getUid($authcode=''){
  52. // 默认接参
  53. $authcode = $authcode ? $authcode : request('authcode','');
  54. // 如果没有有登录信息
  55. if( !$authcode ) return 0;
  56. // 尝试执行
  57. try {
  58. // 解码
  59. $decode = AccessToken::decode($authcode);
  60. // 错误返回
  61. if( isset($decode['error']) ) return 0;
  62. // 如果没有过期时间,过期处理
  63. if( empty($decode['exp']) ) return 0;
  64. // 如果过期,过期处理
  65. if( $decode['exp'] <= time() ) return 0;
  66. // 返回用户ID
  67. return $decode['sub'];
  68. } catch (\Throwable $th) {
  69. // 返回0
  70. return 0;
  71. }
  72. }
  73. /**
  74. * 接口验签
  75. *
  76. * @param [Array] $data 参与验证的参数
  77. *
  78. *
  79. * */
  80. public function verify_sign($data=[]){
  81. // 获取参数
  82. $data = $data ? $data : request()->all();
  83. // 没有签名
  84. if( empty($data['sign']) ) throw new VerifySignException('sign_miss');
  85. // 没有签名
  86. if( empty($data['appid']) ) throw new VerifySignException('appid_miss');
  87. // 传了key
  88. if( !empty($data['key']) ) throw new VerifySignException('appkey_no_allow');
  89. // 没有请求时间
  90. if( empty($data['timestamp']) ) throw new VerifySignException('sign_timeout');
  91. // 请求过期
  92. if( abs(time() -$data['timestamp'] ) > 86400 ) throw new VerifySignException('sign_timeout');
  93. // 已使用过的签名不允许二次使用
  94. $this->checkSignUsed($data['sign']);
  95. // 从配置读取接口验签应用数据
  96. $verify = ['2DEA6A19BDCA383B'=>'FD6F87E89EABAF1063A849D971B68618'];
  97. // 是否有对应的appid
  98. if( !isset($verify[$data['appid']]) ) throw new VerifySignException('appid_nofund');
  99. // appkey
  100. $appkey = $verify[$data['appid']];
  101. // 获得签名参数数据
  102. $sign = $data['sign'];
  103. // 删除传入签名参数
  104. unset($data['sign']);
  105. // 删除上传文件的字段
  106. if( isset($data['file'])) unset($data['file']);
  107. // 键名字典排序
  108. ksort($data);
  109. // 空字符串
  110. $param = '';
  111. // 循环参数对象,拼接url字符串
  112. foreach ( $data as $key => $value ) {
  113. // 如果键值为空(空字符串 空数组 空集合 空对象 字符串0 数字0 NULL FALSE),此参数省略;另 '000' 这类0组成的字符串 不为空
  114. if( strtolower($value) == 'false' || strtolower($value) == 'null' || empty($value) || ( is_numeric($value) && $value == 0 && substr_count($value,'.') == 1 ) ) continue;
  115. // 判断键值是否是数组,对象 ,如果是的话,键值转json字符串
  116. if( is_array( $value ) || is_object($value) ) $value = json_encode($value,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
  117. // 如果键值为真则 键值转md5加密(此处小写) 为假则直接拼接,0特殊处理
  118. $param .= '&'.$key.'='.$value;
  119. }
  120. // 转码
  121. $param = ltrim($param,'&');
  122. // 解码
  123. $param = rawurldecode($param);
  124. // 拼接私钥数据
  125. $param .= '&key='.$appkey;
  126. // 比对签名,数据加密(MD5后转大写),$param.'=>'.strtoupper(md5($param)).'=>'.$sign
  127. if( strtoupper(md5($param)) != $sign) throw new VerifySignException('sign_error');
  128. // 验签通过
  129. return ['success'=>'验签成功'];
  130. }
  131. /**
  132. * 判断签名是否已用
  133. *
  134. * @param [Array] $data 参与验证的参数
  135. *
  136. *
  137. * */
  138. public function checkSignUsed($sign='') {
  139. // 获取签名
  140. $is_used = cache('ApiSign:'.$sign);
  141. // 如果签名已用
  142. if( $is_used ) throw new VerifySignException('sign_used');
  143. // 存储已经验证过的签名
  144. cache(['ApiSign:'.$sign=>$sign],60);
  145. // 成功返回
  146. return ['success'=>'签名可用'];
  147. }
  148. /**
  149. * 接口验签
  150. *
  151. * @param Array $data 参与验证的参数
  152. *
  153. *
  154. * */
  155. public function sign($data) {
  156. // 返回加密结果
  157. return ApiSign::encode($data);
  158. }
  159. }