refactor(ui): Rebuild permission system in vbase.js with scope support

- Add scope parameter to VBase constructor for multi-tenant support
    - Replace hasPermission with checkPerm, checkPermOnResource, checkPermAny, checkPermAll
    - Implement _isAdmin check for global wildcard permissions (*:*)
    - Add _matchPermission with wildcard support (resource:*, *:*)
    - Remove default 404 page from vrouter in root.html
master
veypi 7 days ago
parent fba42193cf
commit b2b24df82e

@ -13,9 +13,7 @@
</head> </head>
<body> <body>
<vrouter loading style="height: 100%; width: 100%;"> <vrouter loading style="height: 100%; width: 100%;"> </vrouter>
<page-404></page-404>
</vrouter>
</body> </body>
</html> </html>

@ -1,7 +1,11 @@
class VBase { class VBase {
constructor(baseURL) { constructor(baseURL, scope) {
this.baseURL = baseURL || ''; if (!baseURL) throw new Error('VBase: baseURL is required');
if (!scope) throw new Error('VBase: scope is required');
this.baseURL = baseURL;
this.scope = scope;
this.tokenKey = 'vbase_access_token'; this.tokenKey = 'vbase_access_token';
this.refreshTokenKey = 'vbase_refresh_token'; this.refreshTokenKey = 'vbase_refresh_token';
this.userKey = 'vbase_user_info'; this.userKey = 'vbase_user_info';
@ -140,18 +144,108 @@ class VBase {
return headers; return headers;
} }
// 检查是否有全局管理员权限 (*:* 或 scope:*:*)
_isAdmin() {
const perms = this.user?.permissions || [];
for (const p of perms) {
const permID = p.permission_id || p;
const resourceID = p.resource_id || '*';
// 必须是全局权限 (resource_id 为 * 或空) 且是通配符权限
if (resourceID === '*' && (permID === '*:*' || permID === `${this.scope}:*:*`)) {
return true;
}
}
return false;
}
// Permission Check // Permission Check
hasPermission(permission) { // 基础权限检查 (permissionID 格式: "resource:action")
checkPerm(permissionID) {
if (!this.user) return false; if (!this.user) return false;
if (this.user.is_admin) return true; if (!permissionID) return true;
if (!permission) return true; // 全局管理员直接通过
const userPerms = this.user.permissions || []; if (this._isAdmin()) return true;
return userPerms.includes(permission);
const perms = this.user.permissions || [];
for (const p of perms) {
if (this._matchPermission(p.permission_id || p, permissionID)) {
return true;
}
}
return false;
}
// 检查对特定资源的权限
// permissionID: "resource:action"
// resourceID: 资源实例ID
checkPermOnResource(permissionID, resourceID) {
if (!this.user) return false;
if (!permissionID) return true;
// 全局管理员直接通过
if (this._isAdmin()) return true;
if (!resourceID) return this.checkPerm(permissionID);
const perms = this.user.permissions || [];
for (const p of perms) {
const permID = p.permission_id || p;
const permResourceID = p.resource_id || '*';
if (this._matchPermission(permID, permissionID)) {
if (permResourceID === '*' || permResourceID === resourceID) {
return true;
}
}
}
return false;
}
// 满足任一权限即可
checkPermAny(...permissionIDs) {
for (const pid of permissionIDs) {
if (this.checkPerm(pid)) return true;
}
return false;
}
// 满足所有权限
checkPermAll(...permissionIDs) {
for (const pid of permissionIDs) {
if (!this.checkPerm(pid)) return false;
}
return true;
}
// 内部方法: 权限匹配
// 支持通配符: *:*, resource:*
// 后端权限可能是 "scope:resource:action" 或 "resource:action"
_matchPermission(storedPerm, wantPerm) {
if (storedPerm === wantPerm) return true;
if (storedPerm === '*:*') return true;
// 从后端存储的权限中提取 resource:action 部分
let havePerm = storedPerm;
const haveParts = storedPerm.split(':');
if (haveParts.length === 3) {
// scope:resource:action -> resource:action
havePerm = `${haveParts[1]}:${haveParts[2]}`;
}
if (havePerm === wantPerm) return true;
// 通配符匹配 (resource:* 匹配 resource:any_action)
const [haveRes, haveAct] = havePerm.split(':');
const [wantRes, wantAct] = wantPerm.split(':');
if (haveRes === '*' || (haveRes === wantRes && (haveAct === '*' || haveAct === wantAct))) {
return true;
}
return false;
} }
hasRole(role) { hasRole(role) {
if (!this.user) return false; if (!this.user) return false;
if (this.user.is_admin) return true; // 全局管理员拥有所有角色
if (this._isAdmin()) return true;
const userRoles = this.user.roles || []; const userRoles = this.user.roles || [];
return userRoles.includes(role); return userRoles.includes(role);
} }

Loading…
Cancel
Save