AdminAuthService.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. /**
  3. * ----------------------------------------------------------------------------
  4. * 行到水穷处,坐看云起时
  5. * 开发软件,找贵阳云起信息科技,官网地址:https://www.56q7.com/
  6. * ----------------------------------------------------------------------------
  7. * Author: 老成
  8. * email:85556713@qq.com
  9. */
  10. declare(strict_types=1);
  11. namespace app\admin\service;
  12. use app\common\library\Tree;
  13. use app\common\model\Admin;
  14. use app\common\model\AuthGroup;
  15. use app\common\model\AuthRule;
  16. use app\common\model\QrcodeScan;
  17. use app\common\model\Third;
  18. use app\common\service\AuthService;
  19. use think\facade\Config;
  20. use think\facade\Session;
  21. use think\facade\Cache;
  22. class AdminAuthService extends AuthService{
  23. protected $allowFields = ['id', 'username', 'nickname', 'mobile', 'avatar', 'third_id', 'depart_id', 'groupids', 'status'];
  24. protected $userRuleList = [];
  25. protected $userMenuList = [];
  26. private $platformList=[];
  27. private $modulealis;
  28. private $modulename;
  29. private $controllername;
  30. private $actionname;
  31. protected function init()
  32. {
  33. parent::init();
  34. $this->setUserRuleAndMenu();
  35. }
  36. public function getUserRuleList()
  37. {
  38. return $this->userRuleList;
  39. }
  40. public function getUserMenuList()
  41. {
  42. return $this->userMenuList;
  43. }
  44. public function userinfo(bool $allinfo=false)
  45. {
  46. $r=Session::get('admin');
  47. if(!$r){
  48. return null;
  49. }
  50. if (Config::get('yunqi.login_unique')) {
  51. $my = Admin::get($this->id);
  52. if (!$my || $my['token'] != $r['token']) {
  53. Session::delete("admin");
  54. Session::save();
  55. return null;
  56. }
  57. }
  58. if(Config::get('yunqi.loginip_check')){
  59. if (request()->ip()!=$r['loginip']) {
  60. Session::delete("admin");
  61. Session::save();
  62. return null;
  63. }
  64. }
  65. $r['groupids']=array_map('intval',explode(',',$r['groupids']));
  66. if($allinfo){
  67. return $r;
  68. }
  69. return array_intersect_key($r,array_flip($this->allowFields));
  70. }
  71. public function isSuperAdmin():bool
  72. {
  73. return in_array(1,$this->groupids);
  74. }
  75. public function getElementUi($elementUi)
  76. {
  77. if($this->element_ui){
  78. $data=json_decode($this->element_ui,true);
  79. foreach ($data as $k=>$v){
  80. $elementUi[$k]=$v;
  81. }
  82. }
  83. return $elementUi;
  84. }
  85. public function getChildrenGroupIds(array $groupids=[]):array
  86. {
  87. if(count($groupids)==0){
  88. $groupids=$this->groupids;
  89. }
  90. $list=AuthGroup::where('pid','in',$groupids)->column('id');
  91. $groupids=array_merge($groupids,$list);
  92. if(count($list)>0){
  93. $r=$this->getChildrenGroupIds($list);
  94. $groupids=array_merge($groupids,$r);
  95. }
  96. return array_unique($groupids);
  97. }
  98. public function getRoute(string $type):string
  99. {
  100. switch ($type){
  101. case 'modulealis':
  102. return $this->modulealis;
  103. case 'modulename':
  104. return $this->modulename;
  105. case 'controllername':
  106. return $this->controllername;
  107. case 'actionname':
  108. return $this->actionname;
  109. case 'title':
  110. $rulelist=$this->getRuleList();
  111. $actiontitle=['未定义'];
  112. $rulepid=0;
  113. foreach ($rulelist as $rule){
  114. if($rule['controller']==$this->controllername && !$rule['ismenu']){
  115. $action=json_decode($rule['action'],true);
  116. $title=json_decode($rule['title'],true);
  117. foreach ($action as $key=>$item){
  118. if($item==$this->actionname){
  119. $actiontitle=[$title[$key]];
  120. $rulepid=$rule['pid'];
  121. }
  122. }
  123. }
  124. }
  125. if($rulepid){
  126. $this->getRuleTitles($rulelist,$rulepid,$actiontitle);
  127. return implode('/',array_reverse($actiontitle));
  128. }
  129. return '未定义';
  130. default:
  131. return '';
  132. }
  133. }
  134. public function logout()
  135. {
  136. $admin = Admin::find(intval($this->id));
  137. if ($admin) {
  138. $admin->token = '';
  139. $admin->save();
  140. }
  141. Session::delete("admin");
  142. return true;
  143. }
  144. public function loginByThird(string $__token__,$admin_id,array &$adminlist):bool
  145. {
  146. $scan=QrcodeScan::where(['type'=>'backend-login','foreign_key'=>$__token__])->order('id desc')->find();
  147. if($scan){
  148. $third=Third::where(['platform'=>Third::PLATFORM('微信公众号'),'openid'=>$scan->openid])->find();
  149. if($third){
  150. $list=Admin::where(['third_id'=>$third->id])->select();
  151. $adminlist=[];
  152. foreach ($list as $item){
  153. unset($item['password']);
  154. unset($item['salt']);
  155. if($item['status']!='normal'){
  156. continue;
  157. }
  158. $adminlist[]=$item;
  159. }
  160. if(count($adminlist)==1){
  161. $admin=$adminlist[0];
  162. $admin->loginfailure = 0;
  163. $admin->logintime = time();
  164. $admin->loginip = request()->ip();
  165. $admin->token = uuid();
  166. $admin->save();
  167. Session::set('admin',$admin->toArray());
  168. Session::save();
  169. return true;
  170. }
  171. if(count($adminlist)>1 && $admin_id){
  172. foreach ($adminlist as $xitem){
  173. if($xitem['id']==$admin_id){
  174. $admin=$xitem;
  175. $admin->loginfailure = 0;
  176. $admin->logintime = time();
  177. $admin->loginip = request()->ip();
  178. $admin->token = uuid();
  179. $admin->save();
  180. Session::set('admin',$admin->toArray());
  181. Session::save();
  182. return true;
  183. }
  184. }
  185. }
  186. }
  187. }
  188. return false;
  189. }
  190. public function login(string $username, string $password)
  191. {
  192. $admin = Admin::where(['username' => $username])->find();
  193. if (!$admin) {
  194. throw new \Exception('用户名或密码错误!');
  195. }
  196. if ($admin['status'] == 'hidden') {
  197. throw new \Exception('用户已被禁止使用!');
  198. }
  199. if (Config::get('yunqi.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400) {
  200. throw new \Exception('登陆失败次数过多,请一天后重试!');
  201. }
  202. if ($admin->password != md5(md5($password) . $admin->salt)) {
  203. $admin->loginfailure++;
  204. $admin->save();
  205. throw new \Exception('用户名或密码错误!');
  206. }
  207. $admin->loginfailure = 0;
  208. $admin->logintime = time();
  209. $admin->loginip = request()->ip();
  210. $admin->token = uuid();
  211. $admin->save();
  212. Session::set('admin',$admin->toArray());
  213. Session::save();
  214. return $admin;
  215. }
  216. public function getRuleList()
  217. {
  218. $rule = AuthRule::field('id,pid,status,controller,action,title,icon,menutype,ismenu,isplatform,extend')
  219. ->order('weigh', 'desc')
  220. ->cache('admin_rule_list')
  221. ->select()
  222. ->toArray();
  223. foreach ($rule as $k=>$v) {
  224. if($v['ismenu'] && $v['status']=='hidden'){
  225. unset($rule[$k]);
  226. continue;
  227. }
  228. if($v['ismenu'] && $v['status']=='normal'){
  229. $rule[$k]['url'] = $this->getPath($v['controller'], $v['action']);
  230. }
  231. }
  232. return $rule;
  233. }
  234. /**
  235. * 根据控制器注解获取到菜单栏的path
  236. * @param string $controller
  237. * @param string $action
  238. * @return string
  239. */
  240. public function getPath(mixed $controller,mixed $action):string
  241. {
  242. $url='';
  243. if(!$controller || !$action){
  244. return build_url('404');
  245. }
  246. if(!class_exists($controller) || !method_exists($controller,$action)){
  247. return build_url('404');
  248. }
  249. $class=new \ReflectionClass($controller);
  250. $attributes=$class->getAttributes();
  251. foreach ($attributes as $attribute)
  252. {
  253. $name=$attribute->getName();
  254. if($name=='think\annotation\route\Group'){
  255. $url=$attribute->getArguments()[0].'/';
  256. break;
  257. }
  258. }
  259. $method=new \ReflectionMethod($controller, $action);
  260. $attributes=$method->getAttributes();;
  261. foreach ($attributes as $attribute)
  262. {
  263. $name=$attribute->getName();
  264. if($name=='think\annotation\route\Get' || $name=='think\annotation\route\Post'){
  265. $url=$url.$attribute->getArguments()[0];
  266. break;
  267. }
  268. if($name=='think\annotation\route\Route'){
  269. $url=$url.$attribute->getArguments()[1];
  270. break;
  271. }
  272. }
  273. return build_url($url);
  274. }
  275. private function getRuleTitles(array $rulelist,int $ruleid,array &$actiontitle)
  276. {
  277. foreach ($rulelist as $rule){
  278. if($rule['id']==$ruleid){
  279. $actiontitle[]=$rule['title'];
  280. if($rule['pid']){
  281. $this->getRuleTitles($rulelist,$rule['pid'],$actiontitle);
  282. }
  283. }
  284. }
  285. }
  286. /**
  287. * 为用户权限列表ruleList赋值
  288. */
  289. private function setUserRuleAndMenu()
  290. {
  291. if($this->id){
  292. $adminRuleList= Cache::get('admin_rule_list_'.$this->id);
  293. $adminMenuList= Cache::get('admin_menu_list_'.$this->id);
  294. $platformList=Cache::get('admin_platform_list_'.$this->id);
  295. $rulelist=$this->getRuleList();
  296. if(!$adminRuleList || !$adminMenuList || $platformList || Config::get('app.app_debug')){
  297. $rules=array_column($rulelist,null,'id');
  298. $groups=AuthGroup::column('auth_rules','id');;
  299. foreach ($groups as $key=>$value){
  300. $value=explode(',',$value);
  301. $groups[$key]=array_filter(array_map(function($v) use ($rules){
  302. if($v=='*'){
  303. return '*';
  304. }
  305. return isset($rules[$v])?$rules[$v]:'';
  306. },$value),function ($f){
  307. return $f!='';
  308. });
  309. }
  310. $rulesids=[];
  311. $menuids=[];
  312. $platformids=[];
  313. if($this->isSuperAdmin()){
  314. $adminRuleList='*';
  315. $adminMenuList='*';
  316. $platformList=array(['id'=>0,'title'=>'管理平台']);
  317. foreach ($rulelist as $value){
  318. if($value['isplatform']){
  319. array_push($platformList,['id'=>$value['id'],'title'=>$value['title']]);
  320. }
  321. }
  322. }else{
  323. $adminRuleList=[];
  324. $adminMenuList=[];
  325. $platformList=[];
  326. foreach ($this->groupids as $groupid){
  327. foreach ($groups[$groupid] as $value){
  328. if($value['ismenu']===1){
  329. continue;
  330. }
  331. if(in_array($value['id'],$rulesids)){
  332. continue;
  333. }
  334. $adminRuleList[]=$value;
  335. $rulesids[]=$value['id'];
  336. }
  337. foreach ($groups[$groupid] as $value){
  338. if($value['ismenu']===0){
  339. continue;
  340. }
  341. if(in_array($value['id'],$menuids)){
  342. continue;
  343. }
  344. $adminMenuList[]=$value;
  345. $menuids[]=$value['id'];
  346. }
  347. foreach ($groups[$groupid] as $value){
  348. if($value['pid']===0 && $value['isplatform']===0 && !in_array(0,$platformids)){
  349. $platformList[]=['id'=>0,'title'=>'管理平台'];
  350. $platformids[]=0;
  351. }
  352. if($value['isplatform']===0){
  353. continue;
  354. }
  355. if(in_array($value['id'],$platformids)){
  356. continue;
  357. }
  358. $platformList[]=['id'=>$value['id'],'title'=>$value['title']];
  359. $platformids[]=$value['id'];
  360. }
  361. }
  362. }
  363. Cache::set('admin_rule_list_'.$this->id,$adminRuleList);
  364. Cache::set('admin_menu_list_'.$this->id,$adminMenuList);
  365. Cache::set('admin_platform_list_'.$this->id,$platformList);
  366. }
  367. $this->userRuleList=$adminRuleList;
  368. $this->userMenuList=$adminMenuList;
  369. $this->platformList=$platformList;
  370. }
  371. }
  372. /**
  373. * 检测权限
  374. * @param string $controller
  375. * @param string $action
  376. * @return mixed
  377. */
  378. public function check(string $controller,string $action):int
  379. {
  380. if ($this->userRuleList=='*') {
  381. return 1;
  382. }
  383. foreach ($this->userRuleList as $value){
  384. if($value['controller']==$controller){
  385. $actions=json_decode($value['action'],true);
  386. foreach ($actions as $v){
  387. if($v==$action){
  388. return 1;
  389. }
  390. }
  391. }
  392. }
  393. return 0;
  394. }
  395. /**
  396. * 获取左侧和顶部菜单栏
  397. * @return array
  398. */
  399. public function getSidebar(mixed $refererUrl=''):array
  400. {
  401. $ruleList=$this->getRuleList();
  402. foreach ($ruleList as $k => &$v) {
  403. unset($v['controller']);
  404. unset($v['action']);
  405. if($this->userMenuList!='*' && !in_array($v['id'],array_column($this->userMenuList,'id'))){
  406. unset($ruleList[$k]);
  407. continue;
  408. }
  409. if (!$v['ismenu']) {
  410. unset($ruleList[$k]);
  411. continue;
  412. }
  413. if ($v['isplatform']) {
  414. unset($ruleList[$k]);
  415. continue;
  416. }
  417. $v['title'] = __($v['title']);
  418. if($v['extend']){
  419. $v['extend']=json_decode($v['extend'],true);
  420. }
  421. }
  422. $ruleList=array_values($ruleList);
  423. $platform_id=$this->getPlatformId();
  424. $treeRuleList=Tree::instance()->init($ruleList)->getTreeArray($platform_id);
  425. $selected=[];
  426. $referer=[];
  427. $this->getSelectAndReferer($treeRuleList,$refererUrl,$selected,$referer);
  428. if($selected==$referer){
  429. $referer=[];
  430. }
  431. return [$this->platformList,$treeRuleList,$selected,$referer];
  432. }
  433. private function getPlatformId()
  434. {
  435. if($this->platform_id){
  436. foreach ($this->platformList as &$value){
  437. if($value['id']==$this->platform_id){
  438. $value['active']=1;
  439. return $this->platform_id;
  440. }
  441. }
  442. }
  443. $this->platformList[0]['active']=1;
  444. return $this->platformList[0]['id'];
  445. }
  446. private function getSelectAndReferer($treeRuleList,$refererUrl,&$selected,&$referer)
  447. {
  448. foreach ($treeRuleList as $value){
  449. if(count($value['childlist'])===0 && !isset($selected['url'])){
  450. $selected=$value;
  451. }
  452. if($refererUrl){
  453. if(parse_url($refererUrl,PHP_URL_PATH)==parse_url($value['url'],PHP_URL_PATH)){
  454. $value['url']=$refererUrl;
  455. $referer=$value;
  456. }
  457. }
  458. if(count($value['childlist'])>0){
  459. $this->getSelectAndReferer($value['childlist'],$refererUrl,$selected,$referer);
  460. }
  461. }
  462. }
  463. public function getRuleId()
  464. {
  465. foreach ($this->getUserRuleList() as $rule){
  466. if($rule['controller']==$this->controllername){
  467. $action=json_decode($rule['action'],true);
  468. if(in_array($this->actionname,$action)){
  469. return $rule['id'];
  470. }
  471. }
  472. }
  473. return null;
  474. }
  475. public function getBackendAuth()
  476. {
  477. $userlist=$this->userRuleList;
  478. //如果$userlist是数组
  479. if(is_array($userlist)){
  480. foreach ($userlist as $key=>$value){
  481. $userlist[$key]['action']=json_decode($value['action'],true);
  482. $userlist[$key]['title']=json_decode($value['title'],true);
  483. }
  484. }
  485. return [
  486. 'admin'=>$this->userinfo(),
  487. 'rules_list'=>$userlist
  488. ];
  489. }
  490. public function loginByMobile(string $mobile, string $code)
  491. {
  492. }
  493. public function loginByThirdPlatform(string $platform, Third $third)
  494. {
  495. }
  496. }