|
| 1 | +<?php |
| 2 | +// +---------------------------------------------------------------------- |
| 3 | +// | ThinkPHP [ WE CAN DO IT JUST THINK IT ] |
| 4 | +// +---------------------------------------------------------------------- |
| 5 | +// | Copyright (c) 2011 http://thinkphp.cn All rights reserved. |
| 6 | +// +---------------------------------------------------------------------- |
| 7 | +// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) |
| 8 | +// +---------------------------------------------------------------------- |
| 9 | +namespace wamkj\thinkphp; |
| 10 | +/** |
| 11 | + * 权限认证类 |
| 12 | + * 功能特性: |
| 13 | + * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。 |
| 14 | + * $auth=new Auth(); $auth->check('规则名称','用户id') |
| 15 | + * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and) |
| 16 | + * $auth=new Auth(); $auth->check('规则1,规则2','用户id','and') |
| 17 | + * 第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or |
| 18 | + * 3,一个用户可以属于多个用户组(tp_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(tp_auth_group 定义了用户组权限) |
| 19 | + * |
| 20 | + * 4,支持规则表达式。 |
| 21 | + * 在tp_auth_rule 表中定义一条规则时,如果type为1, condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100 表示用户的分数在5-100之间时这条规则才会通过。 |
| 22 | + */ |
| 23 | + |
| 24 | +//数据库 |
| 25 | +/* |
| 26 | +-- ---------------------------- |
| 27 | +-- tp_admin,用户表, |
| 28 | +-- id:主键,is_admin:是否是管理员 |
| 29 | +-- ---------------------------- |
| 30 | + DROP TABLE IF EXISTS `tp_admin`; |
| 31 | +CREATE TABLE `tp_admin` ( |
| 32 | + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '管理员ID', |
| 33 | + `is_admin` tinyint(1) NOT NULL DEFAULT '0', |
| 34 | + `username` varchar(50) NOT NULL DEFAULT '' COMMENT '管理员用户名', |
| 35 | + `fullname` varchar(50) NOT NULL DEFAULT '', |
| 36 | + `phone` varchar(20) NOT NULL DEFAULT '', |
| 37 | + `password_reset_token` varchar(255) NOT NULL DEFAULT '', |
| 38 | + `access_token` varchar(32) NOT NULL DEFAULT '', |
| 39 | + `email` varchar(255) NOT NULL DEFAULT '' COMMENT '邮箱', |
| 40 | + `password` varchar(32) NOT NULL DEFAULT '' COMMENT '管理员密码', |
| 41 | + `login_times` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '登陆次数', |
| 42 | + `login_ip` varchar(20) NOT NULL DEFAULT '' COMMENT 'IP地址', |
| 43 | + `login_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '登陆时间', |
| 44 | + `last_login_ip` varchar(255) NOT NULL DEFAULT '' COMMENT '上次登陆ip', |
| 45 | + `last_login_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上次登陆时间', |
| 46 | + `user_agent` varchar(500) NOT NULL DEFAULT '' COMMENT 'user_agent', |
| 47 | + `create_time` int(10) unsigned NOT NULL DEFAULT '0', |
| 48 | + `update_time` int(10) NOT NULL DEFAULT '0', |
| 49 | + `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1可用0禁用', |
| 50 | + PRIMARY KEY (`id`) |
| 51 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; |
| 52 | +-- ---------------------------- |
| 53 | +-- tp_auth_rule,规则表, |
| 54 | +-- id:主键,name:规则唯一标识, title:规则中文名称 status 状态:为1正常,为0禁用,condition:规则表达式,为空表示存在就验证,不为空表示按照条件验证 |
| 55 | +-- ---------------------------- |
| 56 | + DROP TABLE IF EXISTS `tp_auth_rule`; |
| 57 | +CREATE TABLE `tp_auth_rule` ( |
| 58 | + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, |
| 59 | + `name` char(80) NOT NULL DEFAULT '', |
| 60 | + `title` char(20) NOT NULL DEFAULT '', |
| 61 | + `type` tinyint(1) NOT NULL DEFAULT '1', |
| 62 | + `status` tinyint(1) NOT NULL DEFAULT '1', |
| 63 | + `condition` char(100) NOT NULL DEFAULT '', # 规则附件条件,满足附加条件的规则,才认为是有效的规则 |
| 64 | + PRIMARY KEY (`id`), |
| 65 | + UNIQUE KEY `name` (`name`) |
| 66 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; |
| 67 | +-- ---------------------------- |
| 68 | +-- tp_auth_group 用户组表, |
| 69 | +-- id:主键, title:用户组中文名称, rules:用户组拥有的规则id, 多个规则","隔开,status 状态:为1正常,为0禁用 |
| 70 | +-- ---------------------------- |
| 71 | + DROP TABLE IF EXISTS `tp_auth_group`; |
| 72 | +CREATE TABLE `tp_auth_group` ( |
| 73 | + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, |
| 74 | + `title` char(100) NOT NULL DEFAULT '', |
| 75 | + `status` tinyint(1) NOT NULL DEFAULT '1', |
| 76 | + `rules` char(80) NOT NULL DEFAULT '', |
| 77 | + PRIMARY KEY (`id`) |
| 78 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; |
| 79 | +-- ---------------------------- |
| 80 | +-- tp_auth_group_access 用户组明细表 |
| 81 | +-- uid:用户id,group_id:用户组id |
| 82 | +-- ---------------------------- |
| 83 | +DROP TABLE IF EXISTS `tp_auth_group_access`; |
| 84 | +CREATE TABLE `tp_auth_group_access` ( |
| 85 | + `uid` mediumint(8) unsigned NOT NULL, |
| 86 | + `group_id` mediumint(8) unsigned NOT NULL, |
| 87 | + UNIQUE KEY `uid_group_id` (`uid`,`group_id`), |
| 88 | + KEY `uid` (`uid`), |
| 89 | + KEY `group_id` (`group_id`) |
| 90 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; |
| 91 | + */ |
| 92 | + |
| 93 | +class Auth{ |
| 94 | + |
| 95 | + //默认配置 |
| 96 | + protected $_config = array( |
| 97 | + 'auth_on' => true, // 认证开关 |
| 98 | + 'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。 |
| 99 | + 'auth_group' => 'auth_group', // 用户组数据表名 |
| 100 | + 'auth_group_access' => 'auth_group_access', // 用户-用户组关系表 |
| 101 | + 'auth_rule' => 'auth_rule', // 权限规则表 |
| 102 | + 'auth_user' => 'admin' // 用户信息表 |
| 103 | + ); |
| 104 | + |
| 105 | + public function __construct() { |
| 106 | + $prefix = config('database.prefix'); |
| 107 | + $this->_config['auth_group'] = $prefix.$this->_config['auth_group']; |
| 108 | + $this->_config['auth_rule'] = $prefix.$this->_config['auth_rule']; |
| 109 | + $this->_config['auth_user'] = $prefix.$this->_config['auth_user']; |
| 110 | + $this->_config['auth_group_access'] = $prefix.$this->_config['auth_group_access']; |
| 111 | + if (config('auth.auth_config')) { |
| 112 | + //可设置配置项 auth_config, 此配置项为数组。 |
| 113 | + $this->_config = array_merge($this->_config, config('auth.auth_config')); |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + /** |
| 118 | + * 检查权限 |
| 119 | + * @param name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 |
| 120 | + * @param uid int 认证用户的id |
| 121 | + * @param string mode 执行check的模式 |
| 122 | + * @param relation string 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证 |
| 123 | + * @return boolean 通过验证返回true;失败返回false |
| 124 | + */ |
| 125 | + public function check($name, $uid, $type=1, $mode='url', $relation='or') { |
| 126 | + if (!$this->_config['auth_on']) |
| 127 | + return true; |
| 128 | + $authList = $this->getAuthList($uid,$type); //获取用户需要验证的所有有效规则列表 |
| 129 | + if (is_string($name)) { |
| 130 | + $name = strtolower($name); |
| 131 | + if (strpos($name, ',') !== false) { |
| 132 | + $name = explode(',', $name); |
| 133 | + } else { |
| 134 | + $name = array($name); |
| 135 | + } |
| 136 | + } |
| 137 | + $list = array(); //保存验证通过的规则名 |
| 138 | + if ($mode=='url') { |
| 139 | + $REQUEST = unserialize( strtolower(serialize($_REQUEST)) ); |
| 140 | + } |
| 141 | + foreach ( $authList as $auth ) { |
| 142 | + $query = preg_replace('/^.+\?/U','',$auth); |
| 143 | + if ($mode=='url' && $query!=$auth ) { |
| 144 | + parse_str($query,$param); //解析规则中的param |
| 145 | + $intersect = array_intersect_assoc($REQUEST,$param); |
| 146 | + $auth = preg_replace('/\?.*$/U','',$auth); |
| 147 | + if ( in_array($auth,$name) && $intersect==$param ) { //如果节点相符且url参数满足 |
| 148 | + $list[] = $auth ; |
| 149 | + } |
| 150 | + }else if (in_array($auth , $name)){ |
| 151 | + $list[] = $auth ; |
| 152 | + } |
| 153 | + } |
| 154 | + if ($relation == 'or' and !empty($list)) { |
| 155 | + return true; |
| 156 | + } |
| 157 | + $diff = array_diff($name, $list); |
| 158 | + if ($relation == 'and' and empty($diff)) { |
| 159 | + return true; |
| 160 | + } |
| 161 | + return false; |
| 162 | + } |
| 163 | + |
| 164 | + /** |
| 165 | + * 根据用户id获取用户组,返回值为数组 |
| 166 | + * @param uid int 用户id |
| 167 | + * @return array 用户所属的用户组 array( |
| 168 | + * array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'), |
| 169 | + * ...) |
| 170 | + */ |
| 171 | + public function getGroups($uid) { |
| 172 | + static $groups = array(); |
| 173 | + if (isset($groups[$uid])) |
| 174 | + return $groups[$uid]; |
| 175 | + $user_groups = \think\facade\Db::table($this->_config['auth_group_access']) |
| 176 | + ->alias('a') |
| 177 | + ->where("a.uid='$uid' and g.status='1'") |
| 178 | + ->join($this->_config['auth_group'].' g','a.group_id=g.id') |
| 179 | + ->field('uid,group_id,title,rules') |
| 180 | + ->select(); |
| 181 | + $groups[$uid]=$user_groups?:array(); |
| 182 | + return $groups[$uid]; |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * 获得权限列表 |
| 187 | + * @param integer $uid 用户id |
| 188 | + * @param integer $type |
| 189 | + */ |
| 190 | + protected function getAuthList($uid,$type) { |
| 191 | + static $_authList = array(); //保存用户验证通过的权限列表 |
| 192 | + $t = implode(',',(array)$type); |
| 193 | + if (isset($_authList[$uid.$t])) { |
| 194 | + return $_authList[$uid.$t]; |
| 195 | + } |
| 196 | + if( $this->_config['auth_type']==2 && isset($_SESSION['_auth_list_'.$uid.$t])){ |
| 197 | + return $_SESSION['_auth_list_'.$uid.$t]; |
| 198 | + } |
| 199 | + |
| 200 | + //读取用户所属用户组 |
| 201 | + $groups = $this->getGroups($uid); |
| 202 | + $ids = array();//保存用户所属用户组设置的所有权限规则id |
| 203 | + foreach ($groups as $g) { |
| 204 | + $ids = array_merge($ids, explode(',', trim($g['rules'], ','))); |
| 205 | + } |
| 206 | + $ids = array_unique($ids); |
| 207 | + if (empty($ids)) { |
| 208 | + $_authList[$uid.$t] = array(); |
| 209 | + return array(); |
| 210 | + } |
| 211 | + $map[] = ['id','in',$ids]; |
| 212 | + $map[] = ['type','=',$type]; |
| 213 | + $map[] = ['status','=',1]; |
| 214 | + //读取用户组所有权限规则 |
| 215 | + $rules = \think\facade\Db::table($this->_config['auth_rule'])->where($map)->field('condition,name')->select(); |
| 216 | + |
| 217 | + //循环规则,判断结果。 |
| 218 | + $authList = array(); // |
| 219 | + foreach ($rules as $rule) { |
| 220 | + if (!empty($rule['condition'])) { //根据condition进行验证 |
| 221 | + $user = $this->getUserInfo($uid);//获取用户信息,一维数组 |
| 222 | + |
| 223 | + $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']); |
| 224 | + //dump($command);//debug |
| 225 | + @(eval('$condition=(' . $command . ');')); |
| 226 | + if ($condition) { |
| 227 | + $authList[] = strtolower($rule['name']); |
| 228 | + } |
| 229 | + } else { |
| 230 | + //只要存在就记录 |
| 231 | + $authList[] = strtolower($rule['name']); |
| 232 | + } |
| 233 | + } |
| 234 | + $_authList[$uid.$t] = $authList; |
| 235 | + if($this->_config['auth_type']==2){ |
| 236 | + //规则列表结果保存到session |
| 237 | + $_SESSION['_auth_list_'.$uid.$t]=$authList; |
| 238 | + } |
| 239 | + return array_unique($authList); |
| 240 | + } |
| 241 | + |
| 242 | + /** |
| 243 | + * 获得用户资料,根据自己的情况读取数据库 |
| 244 | + */ |
| 245 | + protected function getUserInfo($uid) { |
| 246 | + static $userinfo=array(); |
| 247 | + if(!isset($userinfo[$uid])){ |
| 248 | + $userinfo[$uid]=\think\facade\Db::table($this->_config['auth_user'])->where('uid',$uid)->find(); |
| 249 | + } |
| 250 | + return $userinfo[$uid]; |
| 251 | + } |
| 252 | + |
| 253 | +} |
0 commit comments