Auth.php 7.1 KB

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