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