Auth.php 7.0 KB

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