1
+ <?php
2
+ // +----------------------------------------------------------------------
3
+ // | ThinkPHP 5 [ WE CAN DO IT JUST THINK IT ]
4
+ // +----------------------------------------------------------------------
5
+ // | Copyright (c) 2016 http://www.zzstudio.net All rights reserved.
6
+ // +----------------------------------------------------------------------
7
+ // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
+ // +----------------------------------------------------------------------
9
+ // | Author: Byron Sampson <xiaobo.sun@gzzstudio.net>
10
+ // +----------------------------------------------------------------------
11
+ namespace org ;
12
+
13
+ use think \Db ;
14
+ use think \Config ;
15
+ use think \Session ;
16
+ use think \Request ;
17
+ use think \Loader ;
18
+
19
+ /**
20
+ * 权限认证类
21
+ * 功能特性:
22
+ * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
23
+ * $auth=new Auth(); $auth->check('规则名称','用户id')
24
+ * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and)
25
+ * $auth=new Auth(); $auth->check('规则1,规则2','用户id','and')
26
+ * 第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or
27
+ * 3,一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
28
+ * 4,支持规则表达式。
29
+ * 在think_auth_rule 表中定义一条规则时,如果type为1, condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100
30
+ * 表示用户的分数在5-100之间时这条规则才会通过。
31
+ */
32
+ //数据库
33
+ /*
34
+ -- ----------------------------
35
+ -- think_auth_rule,规则表,
36
+ -- id:主键,name:规则唯一标识, title:规则中文名称 status 状态:为1正常,为0禁用,condition:规则表达式,为空表示存在就验证,不为空表示按照条件验证
37
+ -- ----------------------------
38
+ DROP TABLE IF EXISTS `think_auth_rule`;
39
+ CREATE TABLE `think_auth_rule` (
40
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
41
+ `name` char(80) NOT NULL DEFAULT '',
42
+ `title` char(20) NOT NULL DEFAULT '',
43
+ `type` tinyint(1) NOT NULL DEFAULT '1',
44
+ `status` tinyint(1) NOT NULL DEFAULT '1',
45
+ `condition` char(100) NOT NULL DEFAULT '', # 规则附件条件,满足附加条件的规则,才认为是有效的规则
46
+ PRIMARY KEY (`id`),
47
+ UNIQUE KEY `name` (`name`)
48
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
49
+ -- ----------------------------
50
+ -- think_auth_group 用户组表,
51
+ -- id:主键, title:用户组中文名称, rules:用户组拥有的规则id, 多个规则","隔开,status 状态:为1正常,为0禁用
52
+ -- ----------------------------
53
+ DROP TABLE IF EXISTS `think_auth_group`;
54
+ CREATE TABLE `think_auth_group` (
55
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
56
+ `title` char(100) NOT NULL DEFAULT '',
57
+ `status` tinyint(1) NOT NULL DEFAULT '1',
58
+ `rules` char(80) NOT NULL DEFAULT '',
59
+ PRIMARY KEY (`id`)
60
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
61
+ -- ----------------------------
62
+ -- think_auth_group_access 用户组明细表
63
+ -- uid:用户id,group_id:用户组id
64
+ -- ----------------------------
65
+ DROP TABLE IF EXISTS `think_auth_group_access`;
66
+ CREATE TABLE `think_auth_group_access` (
67
+ `uid` mediumint(8) unsigned NOT NULL,
68
+ `group_id` mediumint(8) unsigned NOT NULL,
69
+ UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
70
+ KEY `uid` (`uid`),
71
+ KEY `group_id` (`group_id`)
72
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
73
+ */
74
+
75
+ class Auth
76
+ {
77
+ /**
78
+ * @var object 对象实例
79
+ */
80
+ protected static $ instance ;
81
+ /**
82
+ * 当前请求实例
83
+ * @var Request
84
+ */
85
+ protected $ request ;
86
+
87
+ //默认配置
88
+ protected $ config = [
89
+ 'auth_on ' => 1 , // 权限开关
90
+ 'auth_type ' => 1 , // 认证方式,1为实时认证;2为登录认证。
91
+ 'auth_group ' => 'auth_group ' , // 用户组数据表名
92
+ 'auth_group_access ' => 'auth_group_access ' , // 用户-用户组关系表
93
+ 'auth_rule ' => 'auth_rule ' , // 权限规则表
94
+ 'auth_user ' => 'member ' , // 用户信息表
95
+ ];
96
+
97
+ /**
98
+ * 类架构函数
99
+ * Auth constructor.
100
+ */
101
+ public function __construct ()
102
+ {
103
+ //可设置配置项 auth, 此配置项为数组。
104
+ if ($ auth = Config::get ('auth ' )) {
105
+ $ this ->config = array_merge ($ this ->config , $ auth );
106
+ }
107
+ // 初始化request
108
+ $ this ->request = Request::instance ();
109
+ }
110
+
111
+ /**
112
+ * 初始化
113
+ * @access public
114
+ * @param array $options 参数
115
+ * @return \think\Request
116
+ */
117
+ public static function instance ($ options = [])
118
+ {
119
+ if (is_null (self ::$ instance )) {
120
+ self ::$ instance = new static ($ options );
121
+ }
122
+
123
+ return self ::$ instance ;
124
+ }
125
+
126
+ /**
127
+ * 检查权限
128
+ * @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
129
+ * @param $uid int 认证用户的id
130
+ * @param int $type 认证类型
131
+ * @param string $mode 执行check的模式
132
+ * @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
133
+ * @return bool 通过验证返回true;失败返回false
134
+ */
135
+ public function check ($ name , $ uid , $ type = 1 , $ mode = 'url ' , $ relation = 'or ' )
136
+ {
137
+ if (!$ this ->config ['auth_on ' ]) {
138
+ return true ;
139
+ }
140
+ // 获取用户需要验证的所有有效规则列表
141
+ $ authList = $ this ->getAuthList ($ uid , $ type );
142
+ if (is_string ($ name )) {
143
+ $ name = strtolower ($ name );
144
+ if (strpos ($ name , ', ' ) !== false ) {
145
+ $ name = explode (', ' , $ name );
146
+ } else {
147
+ $ name = [$ name ];
148
+ }
149
+ }
150
+ $ list = []; //保存验证通过的规则名
151
+ if ('url ' == $ mode ) {
152
+ $ REQUEST = unserialize (strtolower (serialize ($ this ->request ->param ())));
153
+ }
154
+ foreach ($ authList as $ auth ) {
155
+ $ query = preg_replace ('/^.+\?/U ' , '' , $ auth );
156
+ if ('url ' == $ mode && $ query != $ auth ) {
157
+ parse_str ($ query , $ param ); //解析规则中的param
158
+ $ intersect = array_intersect_assoc ($ REQUEST , $ param );
159
+ $ auth = preg_replace ('/\?.*$/U ' , '' , $ auth );
160
+ if (in_array ($ auth , $ name ) && $ intersect == $ param ) {
161
+ //如果节点相符且url参数满足
162
+ $ list [] = $ auth ;
163
+ }
164
+ } else {
165
+ if (in_array ($ auth , $ name )) {
166
+ $ list [] = $ auth ;
167
+ }
168
+ }
169
+ }
170
+ if ('or ' == $ relation && !empty ($ list )) {
171
+ return true ;
172
+ }
173
+ $ diff = array_diff ($ name , $ list );
174
+ if ('and ' == $ relation && empty ($ diff )) {
175
+ return true ;
176
+ }
177
+
178
+ return false ;
179
+ }
180
+
181
+ /**
182
+ * 根据用户id获取用户组,返回值为数组
183
+ * @param $uid int 用户id
184
+ * @return array 用户所属的用户组 array(
185
+ * array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
186
+ * ...)
187
+ */
188
+ public function getGroups ($ uid )
189
+ {
190
+ static $ groups = [];
191
+ if (isset ($ groups [$ uid ])) {
192
+ return $ groups [$ uid ];
193
+ }
194
+ // 转换表名
195
+ $ auth_group_access = Loader::parseName ($ this ->config ['auth_group_access ' ], 1 );
196
+ $ auth_group = Loader::parseName ($ this ->config ['auth_group ' ], 1 );
197
+ // 执行查询
198
+ $ user_groups = Db::view ($ auth_group_access , 'uid,group_id ' )
199
+ ->view ($ auth_group , 'title,rules ' , "{$ auth_group_access }.group_id= {$ auth_group }.id " , 'LEFT ' )
200
+ ->where ("{$ auth_group_access }.uid=' {$ uid }' and {$ auth_group }.status='1' " )
201
+ ->select ();
202
+ $ groups [$ uid ] = $ user_groups ?: [];
203
+
204
+ return $ groups [$ uid ];
205
+ }
206
+
207
+ /**
208
+ * 获得权限列表
209
+ * @param integer $uid 用户id
210
+ * @param integer $type
211
+ * @return array
212
+ */
213
+ protected function getAuthList ($ uid , $ type )
214
+ {
215
+ static $ _authList = []; //保存用户验证通过的权限列表
216
+ $ t = implode (', ' , (array )$ type );
217
+ if (isset ($ _authList [$ uid . $ t ])) {
218
+ return $ _authList [$ uid . $ t ];
219
+ }
220
+ if (2 == $ this ->config ['auth_type ' ] && Session::has ('_auth_list_ ' . $ uid . $ t )) {
221
+ return Session::get ('_auth_list_ ' . $ uid . $ t );
222
+ }
223
+ //读取用户所属用户组
224
+ $ groups = $ this ->getGroups ($ uid );
225
+ $ ids = []; //保存用户所属用户组设置的所有权限规则id
226
+ foreach ($ groups as $ g ) {
227
+ $ ids = array_merge ($ ids , explode (', ' , trim ($ g ['rules ' ], ', ' )));
228
+ }
229
+ $ ids = array_unique ($ ids );
230
+ if (empty ($ ids )) {
231
+ $ _authList [$ uid . $ t ] = [];
232
+
233
+ return [];
234
+ }
235
+ $ map = [
236
+ 'id ' => ['in ' , $ ids ],
237
+ 'type ' => $ type
238
+ ];
239
+ //读取用户组所有权限规则
240
+ $ rules = Db::name ($ this ->config ['auth_rule ' ])->where ($ map )->field ('condition,name ' )->select ();
241
+ //循环规则,判断结果。
242
+ $ authList = []; //
243
+ foreach ($ rules as $ rule ) {
244
+ if (!empty ($ rule ['condition ' ])) {
245
+ //根据condition进行验证
246
+ $ user = $ this ->getUserInfo ($ uid ); //获取用户信息,一维数组
247
+ $ command = preg_replace ('/\{(\w*?)\}/ ' , '$user[ \'\\1 \'] ' , $ rule ['condition ' ]);
248
+ @(eval ('$condition=( ' . $ command . '); ' ));
249
+ if ($ condition ) {
250
+ $ authList [] = strtolower ($ rule ['name ' ]);
251
+ }
252
+ } else {
253
+ //只要存在就记录
254
+ $ authList [] = strtolower ($ rule ['name ' ]);
255
+ }
256
+ }
257
+ $ _authList [$ uid . $ t ] = $ authList ;
258
+ if (2 == $ this ->config ['auth_type ' ]) {
259
+ //规则列表结果保存到session
260
+ Session::set ('_auth_list_ ' . $ uid . $ t , $ authList );
261
+ }
262
+
263
+ return array_unique ($ authList );
264
+ }
265
+
266
+ /**
267
+ * 获得用户资料
268
+ * @param $uid
269
+ * @return mixed
270
+ */
271
+ protected function getUserInfo ($ uid )
272
+ {
273
+ static $ user_info = [];
274
+
275
+ $ user = Db::name ($ this ->config ['auth_user ' ]);
276
+ // 获取用户表主键
277
+ $ _pk = is_string ($ user ->getPk ()) ? $ user ->getPk () : 'uid ' ;
278
+ if (!isset ($ user_info [$ uid ])) {
279
+ $ user_info [$ uid ] = $ user ->where ($ _pk , $ uid )->find ();
280
+ }
281
+
282
+ return $ user_info [$ uid ];
283
+ }
284
+ }
0 commit comments