Files
ulthon_admin/extend/base/common/service/AuthServiceBase.php

292 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}