mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 20:55:32 +08:00
292 lines
8.1 KiB
PHP
292 lines
8.1 KiB
PHP
<?php
|
||
|
||
namespace base\common\service;
|
||
|
||
use app\admin\model\SystemAuthNode;
|
||
use app\admin\service\NodeService;
|
||
use app\common\constants\AdminConstant;
|
||
use think\facade\Cache;
|
||
use think\facade\Config;
|
||
use think\facade\Db;
|
||
|
||
/**
|
||
* 权限验证服务
|
||
* Class AuthService.
|
||
*/
|
||
class AuthServiceBase
|
||
{
|
||
/**
|
||
* 用户ID.
|
||
* @var null
|
||
*/
|
||
protected $adminId = null;
|
||
|
||
/**
|
||
* 默认配置.
|
||
* @var array
|
||
*/
|
||
protected $config = [
|
||
'auth_on' => true, // 权限开关
|
||
'system_admin' => 'system_admin', // 用户表
|
||
'system_auth' => 'system_auth', // 权限表
|
||
'system_auth_node' => 'system_auth_node', // 权限-节点表
|
||
];
|
||
|
||
/**
|
||
* 管理员信息.
|
||
* @var array|\think\Model|null
|
||
*/
|
||
protected $adminInfo;
|
||
|
||
/**
|
||
* 所有节点信息.
|
||
* @var array
|
||
*/
|
||
protected $nodeList;
|
||
|
||
/**
|
||
* 管理员所有授权节点.
|
||
* @var array
|
||
*/
|
||
protected $adminNode;
|
||
|
||
protected $nodeService;
|
||
|
||
protected static $dynamicNodeList = [];
|
||
|
||
/***
|
||
* 构造方法
|
||
* AuthService constructor.
|
||
* @param null $adminId
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
*/
|
||
public function __construct($adminId = null)
|
||
{
|
||
$this->nodeService = new NodeService();
|
||
|
||
if(empty($adminId)){
|
||
$adminId = get_session_admin('id');
|
||
}
|
||
$this->adminId = $adminId;
|
||
$this->adminInfo = $this->getAdminInfo();
|
||
$this->nodeList = $this->getNodeList();
|
||
$this->adminNode = $this->getAdminNode();
|
||
|
||
return $this;
|
||
}
|
||
|
||
public function isSuperAdmin()
|
||
{
|
||
return $this->adminId == AdminConstant::SUPER_ADMIN_ID;
|
||
}
|
||
|
||
/**
|
||
* 检测检测权限.
|
||
* @param null $node
|
||
* @return bool
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
*/
|
||
public function checkNode($node = null)
|
||
{
|
||
if(isset(static::$dynamicNodeList[$node])){
|
||
return static::$dynamicNodeList[$node];
|
||
}
|
||
|
||
// 判断是否需要获取当前节点
|
||
if (empty($node)) {
|
||
$node = $this->getCurrentNode();
|
||
} else {
|
||
$node = $this->parseNodeStr($node);
|
||
}
|
||
|
||
return $this->checkNodeResolved($node);
|
||
}
|
||
|
||
/**
|
||
* 对已规范化(parseNodeStr 后)的单个节点执行完整判定链.
|
||
*
|
||
* checkNode 与 getAdminAllowedNodes 共用此实现,严禁平行复制导致语义漂移。
|
||
* 判定链(顺序与原 checkNode 保持一致):
|
||
* 动态节点黑名单 → 超管直通 → auth_on 开关 → 未注册节点 default_auth_check
|
||
* → 注解 auth=false → status/auth_ids 校验 → 白名单成员判定
|
||
* @param string $node
|
||
* @return bool
|
||
*/
|
||
protected function checkNodeResolved($node)
|
||
{
|
||
// 动态节点开关(按规范化节点命中)
|
||
if (isset(static::$dynamicNodeList[$node])) {
|
||
return static::$dynamicNodeList[$node];
|
||
}
|
||
|
||
// 判断是否为超级管理员
|
||
if ($this->isSuperAdmin()) {
|
||
return true;
|
||
}
|
||
// 判断权限验证开关
|
||
if ($this->config['auth_on'] == false) {
|
||
return true;
|
||
}
|
||
|
||
// 判断是否加入节点控制,优先获取缓存信息
|
||
if (!isset($this->nodeList[$node])) {
|
||
return Config::get('admin.default_auth_check');
|
||
}
|
||
$nodeInfo = $this->nodeList[$node];
|
||
if (!$nodeInfo['auth']) {
|
||
return true;
|
||
}
|
||
// 用户验证,优先获取缓存信息
|
||
if (empty($this->adminInfo) || $this->adminInfo['status'] != 1 || empty($this->adminInfo['auth_ids'])) {
|
||
return false;
|
||
}
|
||
// 判断该节点是否允许访问
|
||
if (in_array($node, $this->adminNode)) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 批量获取管理员对候选节点集合的允许子集.
|
||
*
|
||
* 对每个候选节点走与 checkNode() 完全一致的判定链(共用 checkNodeResolved),
|
||
* 返回其中允许访问的节点(规范化后的形式,保持候选顺序并去重)。
|
||
* 用于 MCP 密钥授权(创建者权限子集)等批量场景。
|
||
* @param array $candidateNodes 候选节点列表(controller/action 形式,内部会做 parseNodeStr 规范化)
|
||
* @param int|null $adminId 管理员ID,为 null 时沿用当前实例的管理员
|
||
* @return array 允许的节点列表
|
||
*/
|
||
public function getAdminAllowedNodes(array $candidateNodes, ?int $adminId = null): array
|
||
{
|
||
if (empty($adminId)) {
|
||
$adminId = $this->adminId;
|
||
}
|
||
// 目标管理员与当前实例不一致时,按目标管理员重建判定上下文
|
||
$service = ($adminId == $this->adminId) ? $this : new static($adminId);
|
||
|
||
$allowedNodes = [];
|
||
foreach ($candidateNodes as $node) {
|
||
$node = $service->parseNodeStr($node);
|
||
if ($service->checkNodeResolved($node) && !in_array($node, $allowedNodes)) {
|
||
$allowedNodes[] = $node;
|
||
}
|
||
}
|
||
|
||
return $allowedNodes;
|
||
}
|
||
|
||
/**
|
||
* 获取当前节点.
|
||
* @return string
|
||
*/
|
||
public function getCurrentNode()
|
||
{
|
||
$node = $this->parseNodeStr(request()->controller() . '/' . request()->action());
|
||
|
||
return $node;
|
||
}
|
||
|
||
/**
|
||
* 获取当前管理员所有节点.
|
||
* @return array
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
*/
|
||
public function getAdminNode()
|
||
{
|
||
$nodeList = [];
|
||
$adminInfo = $this->getAdminInfo();
|
||
|
||
if (!empty($adminInfo) && $adminInfo['status'] != 1) {
|
||
return $nodeList;
|
||
}
|
||
|
||
if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
|
||
$nodeList = SystemAuthNode::where('auth_id', 'in', $adminInfo['auth_ids'])->cache(60)->column('node');
|
||
}
|
||
|
||
return $nodeList;
|
||
}
|
||
|
||
/**
|
||
* 获取所有节点信息.
|
||
* @time 2021-01-07
|
||
* @return array
|
||
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
||
*/
|
||
public function getNodeList()
|
||
{
|
||
$cache_key = 'node_paris';
|
||
|
||
$node_list = Cache::get($cache_key);
|
||
if (!$node_list) {
|
||
$node_list = $this->nodeService->getNodeParis();
|
||
Cache::set($cache_key, $node_list, 60);
|
||
}
|
||
|
||
return $node_list;
|
||
}
|
||
|
||
/**
|
||
* 获取管理员信息.
|
||
* @time 2021-01-07
|
||
* @return array|\think\Model|null
|
||
* @throws \think\db\exception\DataNotFoundException
|
||
* @throws \think\db\exception\DbException
|
||
* @throws \think\db\exception\ModelNotFoundException
|
||
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
||
*/
|
||
public function getAdminInfo()
|
||
{
|
||
return Db::name($this->config['system_admin'])
|
||
->where('id', $this->adminId)
|
||
->autoCache('info', $this->adminId)
|
||
->find();
|
||
}
|
||
|
||
/**
|
||
* 驼峰转下划线规则.
|
||
* @param string $node
|
||
* @return string
|
||
*/
|
||
public function parseNodeStr($node)
|
||
{
|
||
$array = explode('/', $node);
|
||
foreach ($array as $key => $val) {
|
||
if ($key == 0) {
|
||
$val = explode('.', $val);
|
||
foreach ($val as &$vo) {
|
||
$vo = \think\helper\Str::snake(lcfirst($vo));
|
||
}
|
||
$val = implode('.', $val);
|
||
$array[$key] = $val;
|
||
}
|
||
}
|
||
$node = implode('/', $array);
|
||
|
||
return $node;
|
||
}
|
||
|
||
public function disableNode($node, $skipSuperAdmin = true)
|
||
{
|
||
if($this->isSuperAdmin()){
|
||
if($skipSuperAdmin){
|
||
return;
|
||
}
|
||
}
|
||
static::$dynamicNodeList[$node] = false;
|
||
}
|
||
|
||
public function enableNode($node)
|
||
{
|
||
static::$dynamicNodeList[$node] = true;
|
||
}
|
||
}
|