Userauth.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <?php
  2. namespace app\api\controller;
  3. use app\common\controller\Api;
  4. use think\Db;
  5. use TencentCloud\Common\Credential;
  6. use TencentCloud\Common\Profile\ClientProfile;
  7. use TencentCloud\Common\Profile\HttpProfile;
  8. use TencentCloud\Common\Exception\TencentCloudSDKException;
  9. use TencentCloud\Faceid\V20180301\FaceidClient;
  10. use TencentCloud\Faceid\V20180301\Models\IdCardVerificationRequest;
  11. use TencentCloud\Iai\V20200303\IaiClient;
  12. use TencentCloud\Iai\V20200303\Models\CompareFaceRequest;
  13. /**
  14. * 实名认证
  15. */
  16. class Userauth extends Api
  17. {
  18. // 无需登录的接口,*表示全部
  19. protected $noNeedLogin = ['test', 'test1'];
  20. // 无需鉴权的接口,*表示全部
  21. protected $noNeedRight = ['*'];
  22. //实名认证信息
  23. public function idcard_confirm_info(){
  24. $check = Db::name('user_idconfirm')->where('user_id',$this->auth->id)->order('id desc')->find();
  25. if (!$check) {
  26. $check = (object)[];
  27. }
  28. $this->success('success',$check);
  29. }
  30. //实名认证
  31. public function idcard_auth() {
  32. $info = Db::name('user_idconfirm')->where(['user_id' => $this->auth->id])->find();
  33. if (!empty($info) && $info['status'] == 1) {
  34. $this->error('您已通过审核!');
  35. }
  36. $nickname = input('nickname', '', 'trim'); // 姓名
  37. $idcard = input('idcard', '', 'trim'); // 身份证号
  38. if ($nickname === '') {
  39. $this->error('请输入姓名');
  40. }
  41. if (iconv_strlen($nickname, 'utf-8') > 50) {
  42. $this->error('请输入正确姓名');
  43. }
  44. if ($idcard === '') {
  45. $this->error('请输入身份证号');
  46. }
  47. if (iconv_strlen($idcard, 'utf-8') != 18) {
  48. $this->error('请输入正确身份证号');
  49. }
  50. $count = Db::name('user_idconfirm')->where(['idcard' => $idcard, 'user_id' => ['neq', $this->auth->id]])->count('id');
  51. if ($count) {
  52. $this->error('身份证号已存在');
  53. }
  54. $data = [];
  55. $data['truename'] = $nickname;
  56. $data['idcard'] = $idcard;
  57. //腾讯云身份证二要素认证
  58. $auth_restult = $this->userauth_tencent($idcard, $nickname);
  59. if ($auth_restult) {
  60. $data['status'] = 1; //通过
  61. $msg = '认证通过';
  62. } else {
  63. $data['status'] = 0; //不通过
  64. $msg = '自动认证不通过,请等待人工审核';
  65. }
  66. //开启事务
  67. Db::startTrans();
  68. if (!$info) { //未认证
  69. $data["user_id"] = $this->auth->id;
  70. $data["createtime"] = time();
  71. $res = Db::name('user_idconfirm')->insertGetId($data);
  72. } else { //认证被拒绝过
  73. $data['updatetime'] = time();
  74. $res = Db::name('user_idconfirm')->where(['id' => $info['id'], 'user_id' => $this->auth->id])->setField($data);
  75. }
  76. if (!$res) {
  77. Db::rollback();
  78. $this->error('认证失败');
  79. }
  80. $rt = Db::name('user')->where(['id' => $this->auth->id, 'idcard_status' => $this->auth->idcard_status])->setField('idcard_status', $data['status']);
  81. if ($rt === false) {
  82. Db::rollback();
  83. $this->error('认证失败');
  84. }
  85. if ($data['status'] == 1) {
  86. //完成实名认证 +20金币
  87. $task_rs = \app\common\model\TaskLog::tofinish($this->auth->id,4);
  88. if($task_rs === false){
  89. Db::rollback();
  90. $this->error('完成任务赠送奖励失败');
  91. }
  92. //系统消息
  93. $msg_id = \app\common\model\Message::addMessage($this->auth->id,'实名认证','实名认证已经审核通过');
  94. } else {
  95. //系统消息
  96. $msg_id = \app\common\model\Message::addMessage($this->auth->id,'实名认证','实名认证审核不通过');
  97. }
  98. Db::commit();
  99. $this->success($msg);
  100. }
  101. //腾讯云身份证二要素认证
  102. public function userauth_tencent($idcard = '', $nickname = '') {
  103. // require_once 'vendor/autoload.php';
  104. try {
  105. // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
  106. // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
  107. $config = config('tencent_im');
  108. $cred = new Credential($config['SecretId'], $config['SecretKey']);
  109. // 实例化一个http选项,可选的,没有特殊需求可以跳过
  110. $httpProfile = new HttpProfile();
  111. $httpProfile->setEndpoint("faceid.tencentcloudapi.com");
  112. // 实例化一个client选项,可选的,没有特殊需求可以跳过
  113. $clientProfile = new ClientProfile();
  114. $clientProfile->setHttpProfile($httpProfile);
  115. // 实例化要请求产品的client对象,clientProfile是可选的
  116. $client = new FaceidClient($cred, "", $clientProfile);
  117. // 实例化一个请求对象,每个接口都会对应一个request对象
  118. $req = new IdCardVerificationRequest();
  119. $params = array(
  120. "IdCard" => $idcard,
  121. "Name" => $nickname
  122. );
  123. $req->fromJsonString(json_encode($params));
  124. // 返回的resp是一个IdCardVerificationResponse的实例,与请求对象对应
  125. $resp = $client->IdCardVerification($req);
  126. // 输出json格式的字符串回包
  127. // print_r($resp->toJsonString());
  128. $result = json_decode($resp->toJsonString(), true);
  129. if (isset($result['Result']) && $result['Result'] == 0) {
  130. return 1; //通过
  131. } else {
  132. return 0;
  133. }
  134. }
  135. catch(TencentCloudSDKException $e) {
  136. // echo $e;
  137. return 0;
  138. }
  139. }
  140. /////////////////////////////////////////////////////////////
  141. //申请真人认证
  142. public function realauth() {
  143. if ($this->auth->real_status == 1) {
  144. $this->error('您已经真人认证过了~');
  145. }
  146. if ($this->auth->avatar == config('avatar_boy') || $this->auth->avatar == config('avatar_girl')) {
  147. $this->error('请先上传真人头像~');
  148. }
  149. //获取token
  150. $token_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token?app_id='.config('tencent_yun')['secret_id'].'&secret='.config('tencent_yun')['secret_key'].'&grant_type=client_credential&version=1.0.0';
  151. $token_result = file_get_contents($token_url);
  152. if (!$token_result) {
  153. $this->error('您的网络开小差啦1~');
  154. }
  155. $token_result = json_decode($token_result, true);
  156. if ($token_result['code'] != 0) {
  157. $this->error('您的网络开小差啦2~');
  158. }
  159. $token = $token_result['access_token'];
  160. //获取签名鉴权参数ticket
  161. $ticket_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket?app_id='.config('tencent_yun')['secret_id'].'&access_token='.$token.'&type=SIGN&version=1.0.0';
  162. $ticket_result = file_get_contents($ticket_url);
  163. if (!$ticket_result) {
  164. $this->error('您的网络开小差啦3~');
  165. }
  166. $ticket_result = json_decode($ticket_result, true);
  167. if ($ticket_result['code'] != 0) {
  168. $this->error('您的网络开小差啦4~');
  169. }
  170. $ticket = $ticket_result['tickets'][0]['value'];
  171. //获取签名
  172. $sign_data = [
  173. 'wbappid' => config('tencent_yun')['secret_id'],
  174. 'userId' => (string)$this->auth->id,
  175. 'version' => '1.0.0',
  176. 'ticket' => $ticket,
  177. 'nonce' => Random::alnum(32)
  178. ];//p($sign_data);
  179. asort($sign_data); //p($sign_data);//排序
  180. $sign_string = join('', $sign_data);//p($sign_string);
  181. $sign = sha1($sign_string);//p($sign);
  182. //上传身份信息
  183. $orderNo = getMillisecond() . $this->auth->id . mt_rand(1, 1000); //商户请求的唯一标识
  184. $url = 'https://miniprogram-kyc.tencentcloudapi.com/api/server/getAdvFaceId?orderNo=' . $orderNo;
  185. $avatar = one_domain_image($this->auth->avatar);
  186. $avatar = str_replace('https', 'http', $avatar);
  187. $img = file_get_contents($avatar);
  188. $img = str_replace('data:image/jpg;base64', '', $img);
  189. $img = str_replace('\n', '', $img);
  190. $sourcePhotoStr = base64_encode($img);
  191. $data = [
  192. 'webankAppId' => config('tencent_yun')['secret_id'],
  193. 'orderNo' => $orderNo,
  194. 'userId' => (string)$this->auth->id,
  195. 'sourcePhotoStr' => $sourcePhotoStr,
  196. 'sourcePhotoType' => 2,
  197. 'version' => '1.0.0',
  198. 'sign' => $sign,
  199. 'nonce' => $sign_data['nonce']
  200. ];
  201. $rs = curl_post($url,json_encode($data, 320), ['Content-Type: application/json']);
  202. if (!$rs) {
  203. $this->error('您的网络开小差啦5~');
  204. }
  205. $rs = json_decode($rs, true);
  206. if (!$rs || $rs['code'] != 0) {
  207. $this->error('您的网络开小差啦6~');
  208. }
  209. $user_auth = [
  210. 'user_id' => $this->auth->id,
  211. 'certify_id' => $rs['result']['faceId'],
  212. 'out_trade_no' => $data['orderNo'],
  213. 'status' => 0,
  214. 'createtime' => time(),
  215. 'updatetime' => time()
  216. ];
  217. //开启事务
  218. Db::startTrans();
  219. //查询是否认证过
  220. $info = Db::name('user_auth')->where(['user_id' => $this->auth->id])->find();
  221. if ($info) {
  222. $auth_rs = Db::name('user_auth')->where(['id' => $info['id']])->setField($user_auth);
  223. } else {
  224. $auth_rs = Db::name('user_auth')->insertGetId($user_auth);
  225. }
  226. if (!$auth_rs) {
  227. Db::rollback();
  228. $this->error('您的网络开小差啦7~');
  229. }
  230. //修改用户表认证状态
  231. $user_rs = Db::name('user')->where(['id' => $this->auth->id])->setField('real_status', 0);
  232. if ($user_rs === false) {
  233. Db::rollback();
  234. $this->error('您的网络开小差啦8~');
  235. }
  236. Db::commit();
  237. $return_data = [
  238. 'face_id' => $user_auth['certify_id'],
  239. 'order_no' => $user_auth['out_trade_no'],
  240. 'user_id' => (string)$this->auth->id,
  241. 'nonce' => $sign_data['nonce'],
  242. 'sign' => $sign
  243. ];
  244. $this->success('success', $return_data);
  245. }
  246. //查询真人认证结果
  247. public function getrealauthresult() {
  248. $user_auth = Db::name('user_auth')->where(['user_id' => $this->auth->id])->find();
  249. if (!$user_auth) {
  250. $this->success('尚未认证');
  251. }
  252. if ($user_auth['status'] == 1) {
  253. $this->success('真人认证通过');
  254. }
  255. if (!$user_auth['certify_id']) {
  256. $this->success('请先进行真人认证');
  257. }
  258. //获取token
  259. $token_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token?app_id='.config('tencent_yun')['secret_id'].'&secret='.config('tencent_yun')['secret_key'].'&grant_type=client_credential&version=1.0.0';
  260. $token_result = file_get_contents($token_url);
  261. if (!$token_result) {
  262. $this->error('您的网络开小差啦1~');
  263. }
  264. $token_result = json_decode($token_result, true);
  265. if ($token_result['code'] != 0) {
  266. $this->error('您的网络开小差啦2~');
  267. }
  268. $token = $token_result['access_token'];
  269. //获取签名鉴权参数ticket
  270. $ticket_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket?app_id='.config('tencent_yun')['secret_id'].'&access_token='.$token.'&type=SIGN&version=1.0.0';
  271. $ticket_result = file_get_contents($ticket_url);
  272. if (!$ticket_result) {
  273. $this->error('您的网络开小差啦3~');
  274. }
  275. $ticket_result = json_decode($ticket_result, true);
  276. if ($ticket_result['code'] != 0) {
  277. $this->error('您的网络开小差啦4~');
  278. }
  279. $ticket = $ticket_result['tickets'][0]['value'];
  280. //获取签名
  281. $sign_data = [
  282. 'wbappid' => config('tencent_yun')['secret_id'],
  283. 'orderNo' => $user_auth['out_trade_no'],
  284. 'version' => '1.0.0',
  285. 'ticket' => $ticket,
  286. 'nonce' => Random::alnum(32)
  287. ];//p($sign_data);
  288. asort($sign_data); //p($sign_data);//排序
  289. $sign_string = join('', $sign_data);//p($sign_string);
  290. $sign = sha1($sign_string);//p($sign);
  291. //人脸核身结果查询
  292. $url = 'https://miniprogram-kyc.tencentcloudapi.com/api/v2/base/queryfacerecord?orderNo=' . $user_auth['out_trade_no'];
  293. $data = [
  294. 'appId' => config('tencent_yun')['secret_id'],
  295. 'version' => '1.0.0',
  296. 'nonce' => $sign_data['nonce'],
  297. 'orderNo' => $user_auth['out_trade_no'],
  298. 'sign' => $sign
  299. ];
  300. $rs = curl_post($url,json_encode($data, 320), ['Content-Type: application/json']);
  301. if (!$rs) {
  302. $this->error('您的网络开小差啦5~');
  303. }
  304. $rs = json_decode($rs, true);
  305. if (!$rs || $rs['code'] != 0) {
  306. $this->error($rs['msg']);
  307. }
  308. if ($rs['result']['liveRate'] >= 90 && $rs['result']['similarity'] >= 90) {
  309. $edit_data['status'] = 1;
  310. $msg = '真人认证成功';
  311. } else {
  312. $edit_data['status'] = 2;
  313. $edit_data['certify_id'] = '';
  314. $edit_data['out_trade_no'] = '';
  315. $msg = '真人认证失败';
  316. }
  317. $edit_data['updatetime'] = time();
  318. //开启事务
  319. Db::startTrans();
  320. //修改认证信息
  321. $result = Db::name('user_auth')->where(['user_id' => $this->auth->id, 'status' => $user_auth['status']])->setField($edit_data);
  322. if (!$result) {
  323. Db::rollback();
  324. $this->error('查询认证结果失败2');
  325. }
  326. //修改用户信息
  327. $rs = Db::name('user')->where(['id' => $this->auth->id])->setField('real_status', $edit_data['status']);
  328. if (!$rs) {
  329. Db::rollback();
  330. $this->error('查询认证结果失败3');
  331. }
  332. if ($edit_data['status'] == 1) { //通过
  333. //tag任务赠送金币
  334. //真人认证奖励
  335. $task_rs = \app\common\model\TaskLog::tofinish($this->auth->id,20);
  336. if($task_rs === false){
  337. Db::rollback();
  338. $this->error('完成任务赠送奖励失败');
  339. }
  340. //系统消息
  341. $msg_id = \app\common\model\Message::addMessage($this->auth->id,'真人认证','真人认证已经审核通过');
  342. } else {
  343. //系统消息
  344. $msg_id = \app\common\model\Message::addMessage($this->auth->id,'真人认证','真人认证审核不通过');
  345. }
  346. Db::commit();
  347. $this->success($msg);
  348. }
  349. //真人认证后修改头像前比对
  350. public function realavatar_auit() {
  351. if ($this->auth->real_status != 1) {
  352. $this->error('尚未通过真人认证');
  353. }
  354. $avatar = input('avatar', '', 'trim'); //头像地址
  355. if ($avatar === '') {
  356. $this->error('参数缺失');
  357. }
  358. $avatar = one_domain_image($avatar);
  359. $now_avatar = one_domain_image($this->auth->avatar);
  360. if ($avatar == $now_avatar) {
  361. $this->error('头像未改变');
  362. }
  363. //腾讯云人脸识别
  364. $result = $this->face_tencent($now_avatar, $avatar); //1通过 0拒绝
  365. $this->success('结果', $result);
  366. }
  367. //真人认证后修改头像
  368. public function editrealavatar() {
  369. if ($this->auth->real_status != 1) {
  370. $this->error('尚未通过真人认证');
  371. }
  372. $avatar = input('avatar', '', 'trim'); //头像地址
  373. if ($avatar === '') {
  374. $this->error('参数缺失');
  375. }
  376. $avatar = one_domain_image($avatar);
  377. $now_avatar = one_domain_image($this->auth->avatar);
  378. if ($avatar == $now_avatar) {
  379. $this->error('头像未改变');
  380. }
  381. //腾讯云人脸识别
  382. $auit_result = $this->face_tencent($now_avatar, $avatar); //1通过 0拒绝
  383. if ($auit_result != 1) {
  384. $this->success('提示', ['code' => 2]);
  385. }
  386. $data['avatar'] = $avatar;
  387. $user_result = Db::name('user')->where(['id' => $this->auth->id])->setField($data);
  388. if (!$user_result) {
  389. $this->error('修改失败');
  390. }
  391. $this->success('修改成功');
  392. }
  393. //真人认证后修改头像并取消真人认证
  394. public function editrealavatarcancelauit() {
  395. if ($this->auth->real_status != 1) {
  396. $this->error('尚未通过真人认证');
  397. }
  398. $avatar = input('avatar', '', 'trim'); //头像地址
  399. if ($avatar === '') {
  400. $this->error('参数缺失');
  401. }
  402. $avatar = one_domain_image($avatar);
  403. $now_avatar = one_domain_image($this->auth->avatar);
  404. if ($avatar == $now_avatar) {
  405. $this->error('头像未改变');
  406. }
  407. $data['avatar'] = $avatar;
  408. $data['real_status'] = -1;
  409. //开启事务
  410. Db::startTrans();
  411. $user_result = Db::name('user')->where(['id' => $this->auth->id])->setField($data);
  412. if (!$user_result) {
  413. Db::rollback();
  414. $this->error('修改失败');
  415. }
  416. $user_auth_result = Db::name('user_auth')->where(['user_id' => $this->auth->id])->delete();
  417. if (!$user_auth_result) {
  418. Db::rollback();
  419. $this->error('修改失败');
  420. }
  421. Db::commit();
  422. $this->success('修改成功');
  423. }
  424. //腾讯云人脸识别
  425. public function face_tencent($urla = '', $urlb = '') {
  426. // require_once 'vendor/autoload.php';
  427. try {
  428. // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
  429. // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
  430. $config = config('tencent_im');
  431. $cred = new Credential($config['SecretId'], $config['SecretKey']);
  432. // 实例化一个http选项,可选的,没有特殊需求可以跳过
  433. $httpProfile = new HttpProfile();
  434. $httpProfile->setEndpoint("iai.tencentcloudapi.com");
  435. // 实例化一个client选项,可选的,没有特殊需求可以跳过
  436. $clientProfile = new ClientProfile();
  437. $clientProfile->setHttpProfile($httpProfile);
  438. // 实例化要请求产品的client对象,clientProfile是可选的
  439. $client = new IaiClient($cred, "ap-beijing", $clientProfile);
  440. // 实例化一个请求对象,每个接口都会对应一个request对象
  441. $req = new CompareFaceRequest();
  442. $params = array(
  443. "UrlA" => $urla,
  444. "UrlB" => $urlb,
  445. "FaceModelVersion" => "3.0"
  446. );
  447. $req->fromJsonString(json_encode($params));
  448. // 返回的resp是一个CompareFaceResponse的实例,与请求对象对应
  449. $resp = $client->CompareFace($req);
  450. // 输出json格式的字符串回包
  451. // print_r($resp->toJsonString());
  452. $result = json_decode($resp->toJsonString(), true);
  453. //3.0版本误识率千分之一对应分数为40分,误识率万分之一对应分数为50分,误识率十万分之一对应分数为60分。 一般超过50分则可认定为同一人。
  454. if (isset($result['Score']) && $result['Score'] >= 60) {
  455. return 1; //通过
  456. } else {
  457. return 0;
  458. }
  459. }
  460. catch(TencentCloudSDKException $e) {
  461. // echo $e;
  462. return 0;
  463. }
  464. }
  465. }