Auth.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php
  2. namespace ba;
  3. use think\facade\Db;
  4. use think\db\exception\DbException;
  5. use think\db\exception\DataNotFoundException;
  6. use think\db\exception\ModelNotFoundException;
  7. /**
  8. * 权限规则检测类
  9. */
  10. class Auth
  11. {
  12. /**
  13. * 用户有权限的规则节点
  14. */
  15. protected $rules = [];
  16. /**
  17. * 默认配置
  18. * @var array|string[]
  19. */
  20. protected $config = [
  21. 'auth_group' => 'admin_group', // 用户组数据表名
  22. 'auth_group_access' => 'admin_group_access', // 用户-用户组关系表
  23. 'auth_rule' => 'menu_rule', // 权限规则表
  24. ];
  25. /**
  26. * 子菜单规则数组
  27. * @var array
  28. */
  29. protected $childrens = [];
  30. /**
  31. * @param array $config
  32. */
  33. public function __construct(array $config = [])
  34. {
  35. $this->config = array_merge($this->config, $config);
  36. }
  37. /**
  38. * @param $name
  39. * @return mixed|string
  40. */
  41. public function __get($name)
  42. {
  43. return $this->config[$name];
  44. }
  45. /**
  46. * 获取菜单规则列表
  47. * @access public
  48. * @param int $uid 用户ID
  49. * @return array
  50. * @throws DataNotFoundException
  51. * @throws DbException
  52. * @throws ModelNotFoundException
  53. */
  54. public function getMenus(int $uid): array
  55. {
  56. if (!$this->rules) {
  57. $this->getRuleList($uid);
  58. }
  59. if (!$this->rules) {
  60. return [];
  61. }
  62. foreach ($this->rules as $rule) {
  63. $this->childrens[$rule['pid']][] = $rule;
  64. }
  65. if (!isset($this->childrens[0])) {
  66. return [];
  67. }
  68. return $this->getChildren($this->childrens[0]);
  69. }
  70. /**
  71. * 获取数组中所有菜单规则的子规则
  72. * @param array $rules 菜单规则
  73. */
  74. private function getChildren(array $rules): array
  75. {
  76. foreach ($rules as $key => $rule) {
  77. if (array_key_exists($rule['id'], $this->childrens)) {
  78. $rules[$key]['children'] = $this->getChildren($this->childrens[$rule['id']]);
  79. }
  80. }
  81. return $rules;
  82. }
  83. /**
  84. * 检查是否有某权限
  85. * @param string $name 菜单规则的 name,可以传递两个,以','号隔开
  86. * @param int $uid 用户ID
  87. * @param string $relation 如果出现两个 name,是两个都通过(and)还是一个通过即可(or)
  88. * @param string $mode 如果不使用 url 则菜单规则name匹配到即通过
  89. * @return bool
  90. * @throws DataNotFoundException
  91. * @throws DbException
  92. * @throws ModelNotFoundException
  93. */
  94. public function check(string $name, int $uid, string $relation = 'or', string $mode = 'url'): bool
  95. {
  96. // 获取用户需要验证的所有有效规则列表
  97. $rulelist = $this->getRuleList($uid);
  98. if (in_array('*', $rulelist)) {
  99. return true;
  100. }
  101. if ($name) {
  102. $name = strtolower($name);
  103. if (strpos($name, ',') !== false) {
  104. $name = explode(',', $name);
  105. } else {
  106. $name = [$name];
  107. }
  108. }
  109. $list = []; //保存验证通过的规则名
  110. if ('url' == $mode) {
  111. $REQUEST = json_decode(strtolower(json_encode(request()->param(), JSON_UNESCAPED_UNICODE)), true);
  112. }
  113. foreach ($rulelist as $rule) {
  114. $query = preg_replace('/^.+\?/U', '', $rule);
  115. if ('url' == $mode && $query != $rule) {
  116. parse_str($query, $param); //解析规则中的param
  117. $intersect = array_intersect_assoc($REQUEST, $param);
  118. $rule = preg_replace('/\?.*$/U', '', $rule);
  119. if (in_array($rule, $name) && $intersect == $param) {
  120. // 如果节点相符且url参数满足
  121. $list[] = $rule;
  122. }
  123. } else {
  124. if (in_array($rule, $name)) {
  125. $list[] = $rule;
  126. }
  127. }
  128. }
  129. if ('or' == $relation && !empty($list)) {
  130. return true;
  131. }
  132. $diff = array_diff($name, $list);
  133. if ('and' == $relation && empty($diff)) {
  134. return true;
  135. }
  136. return false;
  137. }
  138. /**
  139. * 获得权限规则列表
  140. * @param int $uid 用户id
  141. * @return array
  142. * @throws DataNotFoundException
  143. * @throws DbException
  144. * @throws ModelNotFoundException
  145. */
  146. public function getRuleList(int $uid): array
  147. {
  148. // 静态保存所有用户验证通过的权限列表
  149. static $ruleList = [];
  150. if (isset($ruleList[$uid])) {
  151. return $ruleList[$uid];
  152. }
  153. // 读取用户规则节点
  154. $ids = $this->getRuleIds($uid);
  155. if (empty($ids)) {
  156. $ruleList[$uid] = [];
  157. return [];
  158. }
  159. $where[] = ['status', '=', '1'];
  160. // 如果没有 * 则只获取用户拥有的规则
  161. if (!in_array('*', $ids)) {
  162. $where[] = ['id', 'in', $ids];
  163. }
  164. // 读取用户组所有权限规则
  165. $this->rules = Db::name($this->config['auth_rule'])
  166. ->withoutField(['remark', 'status', 'weigh', 'updatetime', 'createtime'])
  167. ->where($where)
  168. ->order('weigh desc,id asc')
  169. ->select()->toArray();
  170. // 用户规则
  171. $rules = [];
  172. if (in_array('*', $ids)) {
  173. $rules[] = "*";
  174. }
  175. foreach ($this->rules as $key => $rule) {
  176. $rules[$rule['id']] = strtolower($rule['name']);
  177. if (isset($rule['keepalive']) && $rule['keepalive']) {
  178. $this->rules[$key]['keepalive'] = $rule['name'];
  179. }
  180. }
  181. $ruleList[$uid] = $rules;
  182. return array_unique($rules);
  183. }
  184. /**
  185. * 获取权限规则ids
  186. * @param int $uid
  187. * @return array
  188. * @throws DataNotFoundException
  189. * @throws DbException
  190. * @throws ModelNotFoundException
  191. */
  192. public function getRuleIds(int $uid): array
  193. {
  194. // 用户的组别和规则ID
  195. $groups = $this->getGroups($uid);
  196. $ids = [];
  197. foreach ($groups as $g) {
  198. $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
  199. }
  200. return array_unique($ids);
  201. }
  202. /**
  203. * 获取用户所有分组和对应权限规则
  204. * @param int $uid
  205. * @return array
  206. * @throws DataNotFoundException
  207. * @throws DbException
  208. * @throws ModelNotFoundException
  209. */
  210. public function getGroups(int $uid): array
  211. {
  212. static $groups = [];
  213. if (isset($groups[$uid])) {
  214. return $groups[$uid];
  215. }
  216. if ($this->config['auth_group_access']) {
  217. $userGroups = Db::name($this->config['auth_group_access'])
  218. ->alias('aga')
  219. ->join($this->config['auth_group'] . ' ag', 'aga.group_id = ag.id', 'LEFT')
  220. ->field('aga.uid,aga.group_id,ag.id,ag.pid,ag.name,ag.rules')
  221. ->where("aga.uid='$uid' and ag.status='1'")
  222. ->select()->toArray();
  223. } else {
  224. $userGroups = Db::name('user')
  225. ->alias('u')
  226. ->join($this->config['auth_group'] . ' ag', 'u.group_id = ag.id', 'LEFT')
  227. ->field('u.id as uid,u.group_id,ag.id,ag.name,ag.rules')
  228. ->where("u.id='$uid' and ag.status='1'")
  229. ->select()->toArray();
  230. }
  231. $groups[$uid] = $userGroups ?: [];
  232. return $groups[$uid];
  233. }
  234. }