Api.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php namespace App\Http\Controllers\Api;
  2. use App\Exceptions\Api\VerifySignException;
  3. use App\Exceptions\Api\LoginException;
  4. use App\Exceptions\Api\ApiException;
  5. use App\Facades\Servers\Encrypts\AccessToken;
  6. use App\Facades\Servers\Encrypts\ApiSign;
  7. use App\Http\Controllers\Controller;
  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. $port = request('port','');
  25. // 如果没有有登录信息
  26. if( !$authcode ) throw new LoginException("please_login");
  27. // 解码
  28. $decode = AccessToken::decode($authcode);
  29. // 错误返回
  30. if( isset($decode['error']) ) throw new LoginException("login_error");
  31. // 如果没有过期时间,过期处理
  32. if( empty($decode['exp']) ) throw new LoginException("login_exprie");
  33. // 如果过期,过期处理
  34. if( $decode['exp'] <= time() ) throw new LoginException("login_exprie");
  35. // 登录的商户ID
  36. $loginCompanyId = empty($decode['company_id']) ? 0 : $decode['company_id'];
  37. // 如果登录的商户不一致,提醒重新登录
  38. if( $loginCompanyId != request('company_id',0) ) throw new LoginException("please_login");
  39. // 从缓存获取登录数据
  40. $memAuthcode = (new Custom())->getLoginAuthCodeByUid($decode['sub'],$port);
  41. // 不存在登录信息,提示失败
  42. if( !$memAuthcode ) throw new LoginException("login_miss");
  43. // 如果是32位的话
  44. if( strlen($memAuthcode) == 32 ) $authcode = md5($authcode);
  45. // 如果存在登录数据,并且不是当前用户的缓存登录,提示失败
  46. if( $authcode != $memAuthcode ) throw new LoginException("login_miss");
  47. // 返回用户ID
  48. return $decode['sub'];
  49. }
  50. /**
  51. * 登录验证
  52. *
  53. * @return Int 用户ID
  54. *
  55. */
  56. public function getUid($authcode=''){
  57. // 默认接参
  58. $authcode = $authcode ? $authcode : request('authcode','');
  59. // 如果没有有登录信息
  60. if( !$authcode ) return 0;
  61. // 尝试执行
  62. try {
  63. // 解码
  64. $decode = AccessToken::decode($authcode);
  65. // 错误返回
  66. if( isset($decode['error']) ) return 0;
  67. // 如果没有过期时间,过期处理
  68. if( empty($decode['exp']) ) return 0;
  69. // 如果过期,过期处理
  70. if( $decode['exp'] <= time() ) return 0;
  71. // 登录的商户ID
  72. $loginCompanyId = empty($decode['company_id']) ? 0 : $decode['company_id'];
  73. // 如果登录的商户不一致,提醒重新登录
  74. if( $loginCompanyId != request('company_id',0)) return 0;
  75. // 返回用户ID
  76. return $decode['sub'];
  77. } catch (\Throwable $th) {
  78. // 返回0
  79. return 0;
  80. }
  81. }
  82. /**
  83. * 接口验签
  84. *
  85. * @param [Array] $data 参与验证的参数
  86. *
  87. *
  88. * */
  89. public function verify_sign($data=[]){
  90. // 获取参数
  91. $data = $data ? $data : request()->all();
  92. // 没有签名
  93. if( empty($data['sign']) ) throw new VerifySignException('sign_miss');
  94. // 没有签名
  95. if( empty($data['appid']) ) throw new VerifySignException('appid_miss');
  96. // 传了key
  97. if( !empty($data['key']) ) throw new VerifySignException('appkey_no_allow');
  98. // 没有请求时间
  99. if( empty($data['timestamp']) ) throw new VerifySignException('sign_timeout');
  100. // 请求过期
  101. if( abs(time() -$data['timestamp'] ) > 86400 ) throw new VerifySignException('sign_timeout');
  102. // 已使用过的签名不允许二次使用
  103. $this->checkSignUsed($data['sign']);
  104. // 从配置读取接口验签应用数据
  105. $verify = ['2DEA6A19BDCA383B'=>'FD6F87E89EABAF1063A849D971B68618'];
  106. // 是否有对应的appid
  107. if( !isset($verify[$data['appid']]) ) throw new VerifySignException('appid_nofund');
  108. // appkey
  109. $appkey = $verify[$data['appid']];
  110. // 获得签名参数数据
  111. $sign = $data['sign'];
  112. // 删除传入签名参数
  113. unset($data['sign']);
  114. // 删除上传文件的字段
  115. if( isset($data['file'])) unset($data['file']);
  116. // 键名字典排序
  117. ksort($data);
  118. // 空字符串
  119. $param = '';
  120. // 循环参数对象,拼接url字符串
  121. foreach ( $data as $key => $value ) {
  122. // 如果键值为空(空字符串 空数组 空集合 空对象 字符串0 数字0 NULL FALSE),此参数省略;另 '000' 这类0组成的字符串 不为空
  123. if( strtolower($value) == 'false' || strtolower($value) == 'null' || empty($value) || ( is_numeric($value) && $value == 0 && substr_count($value,'.') == 1 ) ) continue;
  124. // 判断键值是否是数组,对象 ,如果是的话,键值转json字符串
  125. if( is_array( $value ) || is_object($value) ) $value = json_encode($value,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
  126. // 如果键值为真则 键值转md5加密(此处小写) 为假则直接拼接,0特殊处理
  127. $param .= '&'.$key.'='.$value;
  128. }
  129. // 转码
  130. $param = ltrim($param,'&');
  131. // 解码
  132. $param = rawurldecode($param);
  133. // 拼接私钥数据
  134. $param .= '&key='.$appkey;
  135. // 比对签名,数据加密(MD5后转大写),$param.'=>'.strtoupper(md5($param)).'=>'.$sign
  136. if( strtoupper(md5($param)) != $sign) throw new VerifySignException('sign_error');
  137. // 验签通过
  138. return ['success'=>'验签成功'];
  139. }
  140. /**
  141. * 判断签名是否已用
  142. *
  143. * @param [Array] $data 参与验证的参数
  144. *
  145. *
  146. * */
  147. public function checkSignUsed($sign='') {
  148. // 获取签名
  149. $is_used = cache('ApiSign:'.$sign);
  150. // 如果签名已用
  151. if( $is_used ) throw new VerifySignException('sign_used');
  152. // 存储已经验证过的签名
  153. cache(['ApiSign:'.$sign=>$sign],60);
  154. // 成功返回
  155. return ['success'=>'签名可用'];
  156. }
  157. /**
  158. * 接口验签
  159. *
  160. * @param Array $data 参与验证的参数
  161. *
  162. *
  163. * */
  164. public function sign($data) {
  165. // 返回加密结果
  166. return ApiSign::encode($data);
  167. }
  168. /**
  169. * 店铺验证
  170. *
  171. */
  172. public function checkShop($uid='', $shopId=''){
  173. // 商户ID
  174. $companyId = request('company_id',0);
  175. // 默认接参
  176. $shopId = $shopId ? $shopId : request('shop_id',0);
  177. // 访问记录
  178. $CustomShopRecord = (new CustomShopRecord());
  179. // 如果没有店铺,获取用户记录最后访问的店铺
  180. if (!$shopId) $shopId = $uid ? $CustomShopRecord->query()->where(['company_id'=>$companyId,'custom_uid'=>$uid])->orderByDesc('visit_time')->value('shop_id') : $shopId;
  181. // 如果还是没有店铺,获取商户下的第一个店铺
  182. if (!$shopId) $shopId = (new Shop())->query()->where(['company_id'=>$companyId])->orderBy('id')->value('id');
  183. // 如果有shopId
  184. if ( $shopId && $uid ) {
  185. // 判断是不是有对应的店铺数据
  186. $recordId = $CustomShopRecord->where(['custom_uid'=>$uid,'company_id'=>$companyId,'shop_id'=>$shopId])->value('id');
  187. // 更新还是新增
  188. $recordId ? $CustomShopRecord->edit($recordId,['visit_time'=>time()]) : $CustomShopRecord->add(['custom_uid'=>$uid,'shop_id'=>$shopId,'company_id'=>$companyId,'visit_time'=>time()]);
  189. }
  190. // 返回
  191. return ['shop_id'=>$shopId,'company_id'=>$companyId];
  192. }
  193. }