nodejs acl的用戶權(quán)限管理詳解
說明
Q: 這個工具用來做什么的呢
A: 用戶有不同的權(quán)限,比如管理員,vip,普通用戶,每個用戶對應(yīng)訪問api,頁面都不一樣
nodejs有兩個比較有名的權(quán)限管理模塊 一個是acl 一個是rbac 綜合對比了一下最終在做項目的時候選擇了acl
功能列表:
- addUserRoles //給某用戶添加角色
- removeUserRoles //移除某用戶角色
- userRoles //獲取某用戶所有角色
- roleUsers //獲取所有是此角色的用戶
- hasRole // 某用戶是否是某角色
- addRoleParents //給某角色增加父角色
- removeRoleParents //移除某覺得的某父角色或所有父角色
- removeRole //移除某角色
- removeResource //移除某資源
- allow //給某些角色增加某些資源的某些權(quán)限
- removeAllow //移除某些角色的某些資源的某些權(quán)限
- allowedPermissions //查詢某人的所有資源及其權(quán)限
- isAllowed //查詢某人是否有某資源的某權(quán)限
- areAnyRolesAllowed //查詢某角色是否有某資源的某權(quán)限
- whatResources //查詢某角色有哪些資源
- middleware //middleware for express
- backend //指定方式(mongo/redis…)
ACL名詞及其主要方法
roles 角色
- removeRole
- addRoleParents
- allow
- removeAllow
resources 資源
- whatResources
- removeResource
permissions 權(quán)限
users 用戶
- allowedPermissions
- isAllowed
- addUserRoles
- removeUserRoles
- userRoles
- roleUsers
- hasRole
- areAnyRolesAllowed
使用方法
- 建立起配置文件
- 用戶登錄后分配相應(yīng)的權(quán)限
- 需要控制的地方使用acl做校檢
配置文件
const Acl = require('acl');
const aclConfig = require('../conf/acl_conf');
module.exports = function (app, express) {
const acl = new Acl(new Acl.memoryBackend()); // eslint-disable-line
acl.allow(aclConfig);
return acl;
};
// acl_conf
module.exports = [
{
roles: 'normal', // 一般用戶
allows: [
{ resources: ['/admin/reserve'], permissions: ['get'] },
]
},
{
roles: 'member', // 會員
allows: [
{ resources: ['/admin/reserve', '/admin/sign'], permissions: ['get'] },
{ resources: ['/admin/reserve/add-visitor', '/admin/reserve/add-visitor-excel', '/admin/reserve/audit', '/admin/sign/ban'], permissions: ['post'] },
]
},
{
roles: 'admin', // 管理
allows: [
{ resources: ['/admin/reserve', '/admin/sign', '/admin/set'], permissions: ['get'] },
{ resources: ['/admin/set/add-user', '/admin/set/modify-user'], permissions: ['post'] },
]
},
{
roles: 'root', // 最高權(quán)限
allows: [
{ resources: ['/admin/reserve', '/admin/sign', '/admin/set'], permissions: ['get'] },
]
}
];
校檢
這里是結(jié)合express做校檢...結(jié)果發(fā)現(xiàn)acl自己提供的中間件太雞肋了,這里就重寫了一個。
function auth() {
return async function (req, res, next) {
let resource = req.baseUrl;
if (req.route) { // 正常在control中使用有route屬性 但是使用app.use則不會有
resource = resource + req.route.path;
}
console.log('resource', resource);
// 容錯 如果訪問的是 /admin/sign/ 后面為 /符號認(rèn)定也為過
if (resource[resource.length - 1] === '/') {
resource = resource.slice(0, -1);
}
let role = await acl.hasRole(req.session.userName, 'root');
if (role) {
return next();
}
let result = await acl.isAllowed(req.session.userName, resource, req.method.toLowerCase());
// if (!result) {
// let err = {
// errorCode: 401,
// message: '用戶未授權(quán)訪問',
// };
// return res.status(401).send(err.message);
// }
next();
};
}
有點要說明的是express.Router支持導(dǎo)出一個Router模塊 再在app.use使用,但是如果你這樣使用 app.use('/admin/user',auth(), userRoute); 那么是在auth這個函數(shù)是獲取不到 req.route 這個屬性的。 因為acl對訪問權(quán)限做的是強匹配,所以需要有一定的容錯
登錄的權(quán)限分配
result為數(shù)據(jù)庫查詢出來的用戶信息,或者后臺api返給的用戶信息,這里的switch可以使用配置文件的形式,因為我這邊本次項目只有三個權(quán)限,所以就在這里簡單寫了一下。
let roleName = 'normal';
switch (result.result.privilege) {
case 0:
roleName = 'admin';
break;
case 1:
roleName = 'normal';
break;
case 2:
roleName = 'member';
break;
}
if (result.result.name === 'Nathan') {
roleName = 'root';
}
req.session['role'] = roleName;
// req.session['role'] = 'root'; // test
acl.addUserRoles(result.result.name, roleName);
// acl.addUserRoles(result.result.name, 'root'); // test
pug頁面中的渲染邏輯控制
在 express+pug中 app.locals.auth= async function(){} 這個寫法在pug渲染的時候是不會得出最終結(jié)果的,因為pug是同步的,那么我如何控制當(dāng)前頁面或者說當(dāng)前頁面的按鈕用戶是否有權(quán)限展示出來, 這里通用的做法有
- 用戶在登錄的時候有一個路由表和組件表 然后在渲染的時候 根據(jù)這個表去渲染
- 在需要權(quán)限控制的地方,使用函數(shù)來判斷用戶是否有權(quán)限訪問
我這里采用的是結(jié)局方案2.因為比較方便, 但是問題來了 express+pug是不支持異步的寫法,而acl提供給我們的全是異步的, 因為時間原因,我沒有去深究里面的判斷,而是采用了一種耦合性比較高但是比較方便的判斷方法.
app.locals.hasRole = function (userRole, path, method = 'get') {
if (userRole === 'root') {
return true;
}
const current = aclConf.find((n) => {
return n['roles'] === userRole;
});
let isFind = false;
for (let i of current.allows) {
const currentPath = i.resources; // 目前數(shù)組第一個為單純的get路由
isFind = currentPath.includes(path);
if (isFind) {
// 如果找到包含該路徑 并且method也對應(yīng)得上 那么則通過
if (i.permissions.includes(method)) {
break;
}
// 如果找到該路徑 但是method對應(yīng)不上 則繼續(xù)找.
continue;
}
}
return isFind;
};
上述代碼頁比較簡單, 去遍歷acl_conf,查找用戶是否有當(dāng)前頁面的或者按鈕的權(quán)限 因為acl_conf在加載的時候就已經(jīng)被寫入內(nèi)存了,所以性能消耗不會特別大。比如下面的例子。
if hasRole(user.role, '/admin/reserve/audit', 'post')
.col.l3.right-align
a.waves-effect.waves-light.btn.margin-right.blue.font12.js-reviewe-ok 同意
a.waves-effect.waves-light.btn.pink.accent-3.font12.js-reviewe-no 拒絕
結(jié)尾
依靠acl這個組件可以快速打造一個用戶的權(quán)限管理模塊。 但是還有個問題 也急速那個app.locals.hasRole函數(shù), 如果你使用removeAllow動態(tài)改變了用戶的權(quán)限表,那么hasRole函數(shù)就很麻煩了。 所以在這種情況下 有以下幾個解決方案
- 從acl源碼入手
- 每次渲染的時候就把數(shù)據(jù)準(zhǔn)備好
const hasBtn1Role = hasRole(user.role, '/xxx','get');
res.render('a.pug',{hasBtn1Role})
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
node.js使用cluster實現(xiàn)多進(jìn)程
本文給大家詳細(xì)介紹了nodejs使用cluster模塊實現(xiàn)多進(jìn)程的方法和步奏,非常的細(xì)致全面,有需要的小伙伴可以參考下2016-03-03
node.js項目如何創(chuàng)建websocket模塊
這篇文章主要介紹了node.js項目如何創(chuàng)建websocket模塊問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-09-09
使用Express+Node.js對mysql進(jìn)行增改查操作?
這篇文章主要介紹了使用Express+Node.js對mysql進(jìn)行增改查操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08
Node.js net模塊功能及事件監(jiān)聽用法分析
這篇文章主要介紹了Node.js net模塊功能及事件監(jiān)聽用法,結(jié)合實例形式分析了net模塊功能及事件監(jiān)聽相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
Nodejs多站點切換Htpps協(xié)議詳解及簡單實例
這篇文章主要介紹了Nodejs多站點切換Htpps協(xié)議詳解及簡單實例的相關(guān)資料,需要的朋友可以參考下2017-02-02

