You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
// +----------------------------------------------------------------------
// | [ Only to facilitate the creation of it]
// +----------------------------------------------------------------------
// | Personal development
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: tekintian <tekintian@gmail.com>
// +----------------------------------------------------------------------
namespace tp5auth;
defined('VIEW_PATH') or define('VIEW_PATH', __DIR__ . DS . "view/");
use think\Cache;
use think\Config;
use think\Loader;
use think\Request;
use think\Session;
use tp5auth\controller\Rbac;
use tp5auth\model\ActionLog;
use tp5auth\model\AuthAccess;
use tp5auth\model\AuthRoleUser;
use tp5auth\model\Menu;
class Auth
{
const PATH = __DIR__;
public $log = true;
public $noNeedCheckRules = []; //不需要检查的路由规则
public function __construct()
{
$this->request = Request::instance();
$this->param = $this->request->param();
$this->module = $this->request->module();
$this->controller = $this->request->controller();
$this->action = $this->request->action();
}
/**
* 加载控制器方法
* @access public
* @param string $name 方法名
* @return mixed
*/
public function autoload($name)
{
$controller = new Rbac($this->request);
if (strtolower($this->controller) == 'auth' && method_exists($controller, $name)) {
return call_user_func([$controller, $name]);
}
return false;
}
/**
* 权限认证
* @access public
* @return mixed
*/
public function auth()
{
$uid = self::sessionGet('user.uid');
$controller = Loader::parseName($this->controller, 1); //字符串命名风格转换
$rule = strtolower("{$this->module}/{$controller}/{$this->action}");
//如果用户角色是1则无需判断
if (empty($uid)) {
return false;
}
if ($uid == 1) {
self::actionLog($rule);
return true;
}
//无需认证
$noNeedCheckRules = array_merge($this->noNeedCheckRules, [$this->module . '/auth/openfile', $this->module . '/auth/cache']);
if (!in_array($rule, $noNeedCheckRules)) {
return self::authCheck($rule, 'or');
} else {
return true;
}
}
/**
* 菜单权限检查
* @access public
* @return array
*/
public static function menuCheck()
{
$uid = self::sessionGet('user.uid');
if (empty($uid)) {
return false;
}
$where['status'] = 1;
if ($uid != 1) {
$authMenu = self::authMenu('', false);
if (array($authMenu)) { //授权菜单ID
$where['id'] = ['in', array_keys($authMenu)];
}
}
$menu = Menu::where($where)->order(["list_order" => "asc", 'id' => 'asc'])->column('*', 'id');
return $menu;
}
/**
* 行为日志检查
* @access public
* @param string $rule 日志规则
* @return array
*/
private function actionLog($rule)
{
//是否需要打开 行为日志检查
if ($this->log === false) {
return true;
}
$logMenu = Cache::get('logMenu');
if (empty($logMenu)) { //缓存日志24小时
$logMenu = Menu::actionLogMenu();
Cache::set('logMenu', $logMenu, 86400);
}
$menu = isset($logMenu[$rule]) ? $logMenu[$rule] : '';
$log = [];
if (empty($menu)) {
return true;
}
//子集行为日志菜单匹配
if (isset($menu['child'])) {
foreach ($menu['child'] as $v) {
if (!empty($v['rule_param'])) {
$condition = '';
$command = preg_replace('/\{(\w*?)\}/', '$this->param[\'\\1\']', $v['rule_param']);
@(eval('$condition=(' . $command . ');'));
if ($condition and $v['request'] == $this->request->method()) {
$log = $v;
}
}
}
}
//父集行为日志菜单匹配
if (empty($log)) {
if ($menu['request'] == $this->request->method()) {
$log = $menu;
}
}
if (!empty($log)) {
return self::createLog($log['log_rule'], $log['name']);
}
return true;
}
/**
* 创建行为日志
* @param string $logrule 行为日志规则
* @param string $title 标题
* @param int $uid 执行者ID
* @return array
*/
public function createLog($logrule, $title)
{
$uid = self::sessionGet('user.uid');
$param = $this->param;
$condition = '';
$command = preg_replace('/\{(\w*?)\}/', '{$param[\'\\1\']}', $logrule);
@(eval('$condition=("' . $command . '");'));
$data = [
'action_ip' => ip2long($this->request->ip()),
'username' => self::sessionGet('user.nickname'),
'create_time' => time(),
'log_url' => '/' . $this->request->pathinfo(),
'log' => $condition,
'user_id' => $uid,
'title' => $title
];
return ActionLog::create($data);
}
/**
* 检查路由权限
* @access public static
* @param string $path 路由
* @param array $param 参数
* @return bool
*/
public static function checkPath($path, $param = [])
{
$uid = self::sessionGet('user.uid');
if ($uid == 1) {
return true;
}
$authMenu = Cache::get('authMenu_' . $uid);
if (!$authMenu) { //存入缓存 授权菜单
$authMenu = self::authMenu();
Cache::set('authMenu_' . $uid, $authMenu, 600);
}
$count = count(explode('/', $path));
if ($count == 2) {
$module = Request::instance()->module();
$path = "$module/$path";
}
$path = strtolower($path);
//是否为超级管理员角色
if ($path === true) {
return true;
} else if ($path === false) {
return false;
}
if ($authMenu === false) {
return false;
}
//验证路由
foreach ($authMenu as $v) {
if ($v['name'] == $path) {
if (empty($v['rule_param'])) { //验证规则为空,表示所有通过
return true;
} else { //如有验证规则,根据规则验证
$condition = false;
$command = preg_replace('/\{(\w*?)\}/', '$param[\'\\1\']', $v['rule_param']);
@(eval('$condition=(' . $command . ');'));
if ($condition) {
return true;
}
}
}
}
return false;
}
/**
* 检查权限
* @access protected
* @param string $url 路由
* @param string $relation
* @return mixed
*/
protected function authCheck($url, $relation = 'or')
{
$rule = array($url);
$list = []; //保存验证通过的规则名)
$param = $this->param;
$rules = self::authMenu(["b.name" => ["in", $rule]]);
//是否为超级管理员角色
if ($rules === true) {
//行为日志
self::actionLog($url);
return true;
} else if ($rules === false) {
return false;
}
foreach ($rules as $rule) {
if (!empty($rule['rule_param'])) { //根据rule_param进行验证
$condition = false;
$command = preg_replace('/\{(\w*?)\}/', '$param[\'\\1\']', $rule['rule_param']);
@(eval('$condition=(' . $command . ');'));
if ($condition) {
$list[] = strtolower($rule['name']);
}
} else {
$list[] = strtolower($rule['name']);
}
}
if ($relation == 'or' and !empty($list)) {
//行为日志
self::actionLog($url);
return true;
}
$diff = array_diff($rule, $list);
if ($relation == 'and' and empty($diff)) {
return true;
}
return false;
}
/**
* 权限访问清单
* @access private
* @param array $where 查询附加条件
* @param bool $default 隐藏的菜单
* @return array
*/
private static function authMenu($where = [], $default = true)
{
$uid = self::sessionGet('user.uid');
$rule = [];
$roleId = AuthRoleUser::hasWhere('authRole', ['`AuthRoleUser`.`user_id`' => $uid, '`AuthRole`.`status`' => 1])
//->fetchSql(1)
->column('role_id');
//echo $roleId;die;
if (in_array(1, $roleId)) {
return true;
}
$roleId = implode(',', $roleId);
//角色权限 or 管理员权限
if ($default === true) {
$rule = AuthAccess::hasWhere('authRole')->where($where)
->where('(AuthAccess.type="admin_url" and AuthAccess.role_id in(:roleId))or(AuthAccess.type="admin" and AuthAccess.role_id =:uid)', ['roleId' => $roleId,
'uid' => $uid]);
} else if ($default === false) {
$rule = AuthAccess::where($where)
->where('(type="admin_url" and role_id in(:roleId))or(type="admin" and role_id =:uid)', ['roleId' => $roleId,
'uid' => $uid]);
}
$rule = $rule->column('*', 'menu_id');
if (empty($rule)) {
return false;
}
return $rule;
}
/**
* 检测用户是否登录
* @return mixed
*/
public static function is_login()
{
$user = self::sessionGet('user');
if (empty($user)) {
return false;
} else {
return self::sessionGet('user_sign') == self::data_auth_sign($user) ? $user : false;
}
}
/**
* 用户登入
* @access private static
* @param int $uid 用户ID
* @param string $nickname 用户昵称
* @return array
*/
public static function login($uid, $nickname)
{
if (empty($uid) && empty($nickname)) {
return false;
}
$session_prefix = Config::get('tp5auth.session_prefix');
$user = [
'uid' => $uid,
'nickname' => $nickname,
'time' => time()
];
Session::set($session_prefix . 'user', $user);
Session::set($session_prefix . 'user_sign', self::data_auth_sign($user));
return true;
}
/**
* 注销
* @access private static
* @return bool
*/
public static function logout()
{
$session_prefix = Config::get('tp5auth.session_prefix');
Session::delete($session_prefix . 'user');
Session::delete($session_prefix . 'user_sign');
return true;
}
/**
* 数据签名认证
* @access private static
* @param array $data 被认证的数据
* @return string 签名
*/
private static function data_auth_sign($data)
{
$code = http_build_query($data); //url编码并生成query字符串
$sign = sha1($code); //生成签名
return $sign;
}
/**
* 读取session
* @access private static
* @param string $path 被认证的数据
* @return mixed
*/
private static function sessionGet($path = '')
{
$session_prefix = Config::get('tp5auth.session_prefix');
$user = Session::get($session_prefix . $path);
return $user;
}
}