RemoteSsoFacadeRepository.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <?php
  2. namespace App\Repositories\Eloquent;
  3. use App\Repositories\Contracts\RemoteSsoInterface;
  4. use Encore\Admin\Facades\Admin;
  5. use GuzzleHttp\Client;
  6. use Illuminate\Http\Request;
  7. use Illuminate\Http\Response;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Log;
  10. use Monolog\Handler\RotatingFileHandler;
  11. use Monolog\Logger;
  12. use Illuminate\Support\Facades\Redis;
  13. /**
  14. * 远程授权登录相关操作
  15. */
  16. class RemoteSsoFacadeRepository extends BaseRepository implements RemoteSsoInterface
  17. {
  18. private $url = "/admin/ssoIndex";
  19. /**
  20. * http://bj.zy.com/admin/remoteSso?task=loginOther&appid=iog%2BhayCB57Kuo4lyuKWZQ%3D%3D&open_id=e%2F%2Fz3xernIiOmHQIqORkBw%3D%3D&version=1.0.0&sign=a6f1e87dd1b21109e36241c1468a638e
  21. * dd(
  22. * http_build_query(array(
  23. * "task"=>"loginOther",
  24. * "appid"=>"iog+hayCB57Kuo4lyuKWZQ==",
  25. * "open_id"=>"e//z3xernIiOmHQIqORkBw==",
  26. * "version"=>"1.0.0",
  27. * "sign"=>"a6f1e87dd1b21109e36241c1468a638e"
  28. * ))
  29. * );
  30. * @param Request $request
  31. * @param Request $response
  32. * @return mixed
  33. * @throws \Exception
  34. */
  35. public function verifyVisitApi(Request $request,Response $response)
  36. {
  37. $sign = $request->get("sign");
  38. $signData = $request->only(array("task","appid","open_id","version","go_url"));
  39. $config = config("console.remote_sso");
  40. if ($config["appid"] != $signData["appid"]){
  41. throw new \Exception("appid非法参数:{$signData["appid"]}");
  42. }
  43. if (Admin::guard()->check() && Admin::user()->third_openid == $signData["open_id"]){
  44. return $this->redirect($this->url,"登录成功");
  45. }
  46. $nowSign=$this->getSign(
  47. array("open_id"=>$signData["open_id"]),
  48. "core",
  49. $signData["task"],
  50. $signData["version"]
  51. );
  52. if ($nowSign != $sign){
  53. throw new \Exception("签名验证不能通过:sign:{$sign}、nowSign:{$nowSign}");
  54. }
  55. $userModelClass = config('admin.database.users_model');
  56. $userModel = new $userModelClass();
  57. $checkUserData = $userModel::query()
  58. ->where(
  59. array(
  60. "third_openid"=>$signData["open_id"]
  61. )
  62. )
  63. ->orderBy("id","desc")->first();
  64. try {
  65. DB::beginTransaction();
  66. $plaintext = "";
  67. $defaultPassword = "";
  68. if (!empty($checkUserData)){
  69. $plaintext = "{$checkUserData->username}@123456";
  70. $defaultPassword = bcrypt($plaintext);
  71. }
  72. if (empty($checkUserData)){
  73. //开始菜单授权
  74. $permissionModelClass = config('admin.database.permissions_model');
  75. $roleModelClass = config('admin.database.roles_model');
  76. $userAvatar = config('admin.default_avatar');
  77. $name = "";//名称
  78. $userName="";//登录账号
  79. $userData = $this->getUserData($signData["open_id"]);//下面需要获取用户账号、昵称、头像
  80. if (isset($userData["user"])){
  81. if (isset($userData["user"]["name"])){
  82. $name = $userData["user"]["name"];
  83. }
  84. $name = empty($name)?$userData["user"]["username"]:$name;
  85. $userName = $userData["user"]["username"];
  86. }
  87. $plaintext = "{$userName}@123456";
  88. $defaultPassword = bcrypt($plaintext);
  89. $checkUserData = $userModel->create(array(
  90. "username"=>$userName,
  91. "password"=>$defaultPassword,
  92. "name"=>$name,
  93. "avatar"=>null,
  94. ));
  95. if (empty($checkUserData)){
  96. throw new \RuntimeException("新增用户信息失败");
  97. }
  98. DB::table($userModel::query()->getModel()->getTable())->where(
  99. array("id"=>$checkUserData->id)
  100. )->update(array(
  101. "third_openid"=>$signData["open_id"]
  102. ));
  103. $roleSlugName = array("RemoteSso");//角色名称 slug
  104. $permissionsSlugName = array("RemoteSso");//权限名称 slug
  105. $permissionModel = new $permissionModelClass();
  106. $roleModel = new $roleModelClass();
  107. foreach ($roleSlugName as $val){
  108. $roleData = $roleModel::query()
  109. ->where(
  110. array(
  111. "slug"=>$val
  112. )
  113. )
  114. ->orderBy("id","desc")->first();
  115. if (!empty($roleData)){
  116. DB::table("admin_role_users")->insert(
  117. array(
  118. "role_id"=>$roleData->id,
  119. "user_id"=>$checkUserData->id,
  120. "created_at"=>date("Y-m-d H:i:s")
  121. )
  122. );
  123. }
  124. }
  125. foreach ($permissionsSlugName as $val){
  126. $permissionData = $permissionModel::query()
  127. ->where(
  128. array(
  129. "slug"=>$val
  130. )
  131. )
  132. ->orderBy("id","desc")->first();
  133. if (!empty($permissionData)){
  134. DB::table("admin_user_permissions")->insert(
  135. array(
  136. "permission_id"=>$permissionData->id,
  137. "user_id"=>$checkUserData->id,
  138. "created_at"=>date("Y-m-d H:i:s")
  139. )
  140. );
  141. }
  142. }
  143. }
  144. // if ($defaultPassword != $checkUserData->password){
  145. // $checkUserData->update(array("password"=>$defaultPassword));
  146. // }
  147. DB::commit();
  148. Admin::guard()->login(
  149. $checkUserData
  150. );
  151. $key = "RemoteSso:".$_SERVER['HTTP_HOST'].":".$checkUserData->id;
  152. Redis::SET($key, true);
  153. return $this->redirect($this->url,"登录成功");
  154. }catch (\Exception $exception){
  155. Log::error("远程授权登录出错",[$exception->getMessage(),$exception->getTrace()]);
  156. DB::rollBack();
  157. return "登录失败";
  158. }
  159. }
  160. public function verifyVisitUrl(Request $request,Response $response)
  161. {
  162. $sign = $request->get("sign");
  163. $signData = $request->only(array("task","appid","open_id","version","go_url"));
  164. $config = config("console.remote_sso");
  165. if ($config["appid"] != $signData["appid"]){
  166. throw new \Exception("appid非法参数:{$signData["appid"]}");
  167. }
  168. $paramsData = $request->except(array("appid","op", "task","version","sign"));
  169. $userModelClass = config('admin.database.users_model');
  170. $userModel = new $userModelClass();
  171. if (Admin::guard()->check() && Admin::user()->third_openid == $signData["open_id"]){
  172. //更新用户信息
  173. $res = DB::table($userModel::query()->getModel()->getTable())->where(
  174. array("id"=>Admin::user()->id)
  175. )->update(array(
  176. "third_openid"=>$signData["open_id"],
  177. "workstation_id"=>$paramsData['outside_c_id'],
  178. "workstation_name"=>$paramsData['visit_title']
  179. ));
  180. return $this->redirect($this->url,"登录成功");
  181. }
  182. $nowSign=$this->getSign(
  183. $paramsData,
  184. "core",
  185. $signData["task"],
  186. $signData["version"]
  187. );
  188. if ($nowSign != $sign){
  189. throw new \Exception("签名验证不能通过:sign:{$sign}、nowSign:{$nowSign}");
  190. }
  191. $checkUserData = $userModel::query()
  192. ->where(
  193. array(
  194. "third_openid"=>$signData["open_id"],
  195. )
  196. )
  197. ->orderBy("id","desc")->first();
  198. try {
  199. DB::beginTransaction();
  200. $plaintext = "";
  201. $defaultPassword = "";
  202. if (!empty($checkUserData)){
  203. $plaintext = "{$checkUserData->username}@123456";
  204. $defaultPassword = bcrypt($plaintext);
  205. }
  206. if (empty($checkUserData)){
  207. //开始菜单授权
  208. $permissionModelClass = config('admin.database.permissions_model');
  209. $roleModelClass = config('admin.database.roles_model');
  210. $userAvatar = config('admin.default_avatar');
  211. $name = "";//名称
  212. $userName="";//登录账号
  213. $userData = $this->getUserData($signData["open_id"]);//下面需要获取用户账号、昵称、头像
  214. if (isset($userData["user"])){
  215. if (isset($userData["user"]["name"])){
  216. $name = $userData["user"]["name"];
  217. }
  218. $name = empty($name)?$userData["user"]["username"]:$name;
  219. $userName = $userData["user"]["username"];
  220. }
  221. $plaintext = "{$userName}@123456";
  222. $defaultPassword = bcrypt($plaintext);
  223. $checkUserData = $userModel->create(array(
  224. "username"=>$userName,
  225. "password"=>$defaultPassword,
  226. "name"=>$name,
  227. "avatar"=>null,
  228. ));
  229. if (empty($checkUserData)){
  230. throw new \RuntimeException("新增用户信息失败");
  231. }
  232. DB::table($userModel::query()->getModel()->getTable())->where(
  233. array("id"=>$checkUserData->id)
  234. )->update(array(
  235. "third_openid"=>$signData["open_id"],
  236. "workstation_id"=>$paramsData['outside_c_id'],
  237. "workstation_name"=>$paramsData['visit_title']
  238. ));
  239. $roleSlugName = array("RemoteSso");//角色名称 slug
  240. $permissionsSlugName = array("RemoteSso");//权限名称 slug
  241. $permissionModel = new $permissionModelClass();
  242. $roleModel = new $roleModelClass();
  243. foreach ($roleSlugName as $val){
  244. $roleData = $roleModel::query()
  245. ->where(
  246. array(
  247. "slug"=>$val
  248. )
  249. )
  250. ->orderBy("id","desc")->first();
  251. if (!empty($roleData)){
  252. DB::table("admin_role_users")->insert(
  253. array(
  254. "role_id"=>$roleData->id,
  255. "user_id"=>$checkUserData->id,
  256. "created_at"=>date("Y-m-d H:i:s")
  257. )
  258. );
  259. }
  260. }
  261. foreach ($permissionsSlugName as $val){
  262. $permissionData = $permissionModel::query()
  263. ->where(
  264. array(
  265. "slug"=>$val
  266. )
  267. )
  268. ->orderBy("id","desc")->first();
  269. if (!empty($permissionData)){
  270. DB::table("admin_user_permissions")->insert(
  271. array(
  272. "permission_id"=>$permissionData->id,
  273. "user_id"=>$checkUserData->id,
  274. "created_at"=>date("Y-m-d H:i:s")
  275. )
  276. );
  277. }
  278. }
  279. }
  280. // if ($defaultPassword != $checkUserData->password){
  281. // $checkUserData->update(array("password"=>$defaultPassword));
  282. // }
  283. DB::commit();
  284. Admin::guard()->login(
  285. $checkUserData
  286. );
  287. $key = "RemoteSso:".$_SERVER['HTTP_HOST'].":".$checkUserData->id;
  288. Redis::SET($key, true);
  289. return $this->redirect($this->url,"登录成功");
  290. }catch (\Exception $exception){
  291. Log::error("远程授权登录出错",[$exception->getMessage(),$exception->getTrace()]);
  292. DB::rollBack();
  293. return "登录失败";
  294. }
  295. }
  296. public function getUserData(string $openId)
  297. {
  298. if (empty($openId)){
  299. throw new \Exception("openId不能为空");
  300. }
  301. $config = config("console.remote_sso");
  302. $url = $config["url"].self::USER_DATA_URL;//http://www.baidu.com/api.php
  303. $query = array(
  304. "op"=>"user",
  305. "task"=>"userData",
  306. "version"=>"1.0.0",
  307. "appid"=>$config["appid"]
  308. );
  309. $sendData = array(
  310. "open_id"=>$openId
  311. );
  312. $postData = $sendData;
  313. $query["sign"] = $this->getSign($postData,$query["op"],$query["task"],$query["version"]);
  314. // $client = new Client();
  315. $url = $url."?".http_build_query($query);
  316. $resultStr = $this->curlPost($url,$postData);
  317. // $response = $client->request('post', $url, [
  318. // 'query' => $query,
  319. // "headers"=>array(
  320. // "Content-type"=>"application/json"
  321. // ),
  322. // "form_params"=>$postData
  323. // ]);
  324. // $resultStr = $response->getBody()->getContents();
  325. return $this->getResult($resultStr);
  326. }
  327. public function getChildWorksation(string $openOcId)
  328. {
  329. if (empty($openOcId)){
  330. throw new \Exception("openOcId不能为空");
  331. }
  332. $config = config("console.wg_map");
  333. $url = $config["url"].self::USER_DATA_URL;//http://www.baidu.com/api.php
  334. $query = array(
  335. "op"=>"visit",
  336. "task"=>"getDownCompanyDatas",
  337. "version"=>"1.0.0",
  338. "appid"=>$config["appid"]
  339. );
  340. $sendData = array(
  341. "open_oc_id"=>$openOcId,
  342. 's_timestamp' => time(),
  343. );
  344. $postData = $sendData;
  345. $query["sign"] = $this->getWgSign($postData,$query["op"],$query["task"],$query["version"]);
  346. // $client = new Client();
  347. $url = $url."?".http_build_query($query);
  348. $resultStr = $this->curlPost($url,$postData);
  349. // $response = $client->request('post', $url, [
  350. // 'query' => $query,
  351. // "headers"=>array(
  352. // "Content-type"=>"application/json"
  353. // ),
  354. // "form_params"=>$postData
  355. // ]);
  356. // $resultStr = $response->getBody()->getContents();
  357. return $this->getResult($resultStr);
  358. }
  359. public function getUserMenuWebsiteData(string $openId)
  360. {$config = config("console.remote_sso");
  361. $url = $config["url"].self::USER_DATA_URL;//http://www.baidu.com/api.php
  362. $query = array(
  363. "op"=>"menu",
  364. "task"=>"getMenu",
  365. "version"=>"1.0.0",
  366. "appid"=>$config["appid"]
  367. );
  368. $sendData = array(
  369. "open_id"=>$openId
  370. );
  371. $query["sign"] = $this->getSign($sendData,$query["op"],$query["task"],$query["version"]);
  372. // $client = new Client();
  373. // $response = $client->request('post', $url, [
  374. // 'query' => $query,
  375. // "headers"=>array(
  376. // "Content-type"=>"application/json"
  377. // ),
  378. // "form_params"=>$sendData
  379. // ]);
  380. // $resultStr = $response->getBody()->getContents();
  381. $url = $url."?".http_build_query($query);
  382. $resultStr = $this->curlPost($url,$sendData);
  383. return $this->getResult($resultStr);
  384. }
  385. public function getSign(array $sendData, string $op = "user", string $task = "getUserInfo", string $version = "1.0.0")
  386. {
  387. $config = config("console.remote_sso");
  388. $appid = $config["appid"];
  389. $secret = $config["secret"];
  390. ksort($sendData);
  391. $str = "";
  392. foreach($sendData as $key =>$value){
  393. $str.= stripslashes($value);
  394. }
  395. $sign = md5($appid. $op . $task . $version . $str. $secret);
  396. return $sign;
  397. }
  398. public function getWgSign(array $sendData, string $op = "user", string $task = "getUserInfo", string $version = "1.0.0")
  399. {
  400. $config = config("console.wg_map");
  401. $appid = $config["appid"];
  402. $secret = $config["secret"];
  403. ksort($sendData);
  404. $str = "";
  405. foreach($sendData as $key =>$value){
  406. $str.= stripslashes($value);
  407. }
  408. $sign = md5($appid. $op . $task . $version . $str. $secret);
  409. return $sign;
  410. }
  411. /**
  412. * 得到结果数据
  413. * @param string $resultStr
  414. * @return mixed
  415. * @throws \Exception
  416. */
  417. private function getResult(string $resultStr){
  418. $this->log("返回值",$resultStr);
  419. $result = json_decode($resultStr,true);
  420. if (json_last_error() !== JSON_ERROR_NONE) {
  421. $this->log("数据格式响应错误",$resultStr);
  422. throw new \Exception("数据格式响应错误".$resultStr);
  423. }
  424. if (empty($result["status"])){
  425. $content = $result["message"] ?? $resultStr;
  426. $this->log("结果信息发生错误",$content);
  427. throw new \Exception("结果信息发生错误".$content);
  428. }
  429. if (!isset($result["data"])){
  430. $content = $resultStr;
  431. $this->log("结果信息格式发送错误",$content);
  432. throw new \Exception("结果信息格式发送错误".$content);
  433. }
  434. return $result["data"];
  435. }
  436. /**
  437. * @param string $msg 消息提示
  438. * @param array|string|object $content 结果内容
  439. * @return void
  440. */
  441. private function log(string $msg,$content)
  442. {
  443. $sqlFile = 'logs/remoteSso/info.log';
  444. (new Logger('remoteSso'))->pushHandler(new RotatingFileHandler(storage_path($sqlFile)))->info($msg,[$content]);
  445. }
  446. private function curlPost($url, $data=null, $file=null)
  447. {
  448. $ch = curl_init($url);
  449. curl_setopt($ch, CURLOPT_TIMEOUT, 60); //设置超时
  450. // if(!empty($file)){
  451. // foreach ($file as $k=>$v){
  452. // if(is_array($v)){
  453. // //多个文件
  454. // foreach ($v as $key=>$val){
  455. // $data[$k.'['.$key.']'] = new CURLFile($val);
  456. // }
  457. // }else{
  458. // //一维
  459. // $data[$k] = new CURLFile($v);
  460. // }
  461. // }
  462. // }
  463. if (0 === strpos(strtolower($url), "https")) {
  464. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //对认证证书来源的检查
  465. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //从证书中检查SSL加密算法是否存在
  466. }
  467. curl_setopt($ch, CURLOPT_POST, true);
  468. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  469. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  470. // curl_setopt($ch, CURLOPT_HTTPHEADER);
  471. $rtn = curl_exec($ch);//CURLOPT_RETURNTRANSFER 不设置 curl_exec返回TRUE 设置 curl_exec返回json(此处) 失败都返回FALSE
  472. curl_close($ch);
  473. return $rtn;
  474. }
  475. /**
  476. * json 形式传参
  477. * @param unknown $url
  478. * @param unknown $data
  479. */
  480. private function curlPostJson($url, $data = null)
  481. {
  482. $headers = array(
  483. "Content-type: application/json;charset='utf-8'",
  484. "Accept: application/json",
  485. "Cache-Control: no-cache",
  486. "Pragma: no-cache",
  487. );
  488. $ch = curl_init($url);
  489. curl_setopt($ch, CURLOPT_TIMEOUT, 60); //设置超时
  490. if (0 === strpos(strtolower($url), "https")) {
  491. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //对认证证书来源的检查
  492. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //从证书中检查SSL加密算法是否存在
  493. }
  494. curl_setopt($ch, CURLOPT_POST, true);
  495. curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
  496. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  497. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  498. curl_setopt($ch, CURLOPT_HEADER, 1);//打印响应header
  499. $rtn = curl_exec($ch);//CURLOPT_RETURNTRANSFER 不设置 curl_exec返回TRUE 设置 curl_exec返回json(此处) 失败都返回FALSE
  500. $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  501. // 根据头大小去获取头信息内容
  502. $header = substr($rtn, 0, $header_size);
  503. $data = substr($rtn, $header_size);
  504. $is_gzipped = false;
  505. if (preg_match("/Content-Encoding: gzip/", $header)) {
  506. $is_gzipped = true;
  507. }
  508. if($is_gzipped){
  509. $data = gzdecode($data);
  510. }
  511. curl_close($ch);
  512. return $data;
  513. }
  514. /**
  515. * @param string $targetUrl
  516. * @param string $msg
  517. * @param int $wait 跳转时间
  518. * @return string
  519. */
  520. private function redirect($targetUrl,$msg,$wait = 3)
  521. {
  522. return <<<HTML
  523. <!DOCTYPE html>
  524. <html lang="en">
  525. <head>
  526. <meta charset="UTF-8">
  527. <title>跳转中...</title>
  528. <style>
  529. body {
  530. font-family: Arial, sans-serif;
  531. background-color: #f4f4f4;
  532. margin: 0;
  533. padding: 0;
  534. display: flex;
  535. justify-content: center;
  536. align-items: center;
  537. height: 100vh;
  538. }
  539. .container {
  540. text-align: center;
  541. background-color: #ffffff;
  542. padding: 20px;
  543. border-radius: 8px;
  544. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  545. }
  546. p {
  547. margin-bottom: 10px;
  548. color: #333333;
  549. }
  550. .loader {
  551. border: 4px solid #f3f3f3;
  552. border-top: 4px solid #3498db;
  553. border-radius: 50%;
  554. width: 20px;
  555. height: 20px;
  556. animation: spin 2s linear infinite;
  557. display: inline-block;
  558. margin-right: 10px;
  559. }
  560. @keyframes spin {
  561. 0% { transform: rotate(0deg); }
  562. 100% { transform: rotate(360deg); }
  563. }
  564. </style>
  565. </head>
  566. <body>
  567. <div class="container">
  568. <div class="loader"></div>
  569. <p>$msg,正在跳转,请稍候...</p>
  570. <meta http-equiv="refresh" content="$wait;url={$targetUrl}">
  571. <script type="text/javascript">
  572. // 如果浏览器不支持 meta 标签自动跳转,则使用 JavaScript 进行跳转
  573. setTimeout(function() {
  574. window.location.href = "{$targetUrl}";
  575. }, 3000);
  576. </script>
  577. </div>
  578. </body>
  579. </html>
  580. HTML;
  581. }
  582. }