nodejs acl的用戶(hù)權(quán)限管理詳解
說(shuō)明
Q: 這個(gè)工具用來(lái)做什么的呢
A: 用戶(hù)有不同的權(quán)限,比如管理員,vip,普通用戶(hù),每個(gè)用戶(hù)對(duì)應(yīng)訪(fǎng)問(wèn)api,頁(yè)面都不一樣
nodejs有兩個(gè)比較有名的權(quán)限管理模塊 一個(gè)是acl 一個(gè)是rbac 綜合對(duì)比了一下最終在做項(xiàng)目的時(shí)候選擇了acl
功能列表:
- addUserRoles //給某用戶(hù)添加角色
- removeUserRoles //移除某用戶(hù)角色
- userRoles //獲取某用戶(hù)所有角色
- roleUsers //獲取所有是此角色的用戶(hù)
- hasRole // 某用戶(hù)是否是某角色
- addRoleParents //給某角色增加父角色
- removeRoleParents //移除某覺(jué)得的某父角色或所有父角色
- removeRole //移除某角色
- removeResource //移除某資源
- allow //給某些角色增加某些資源的某些權(quán)限
- removeAllow //移除某些角色的某些資源的某些權(quán)限
- allowedPermissions //查詢(xún)某人的所有資源及其權(quán)限
- isAllowed //查詢(xún)某人是否有某資源的某權(quán)限
- areAnyRolesAllowed //查詢(xún)某角色是否有某資源的某權(quán)限
- whatResources //查詢(xún)某角色有哪些資源
- middleware //middleware for express
- backend //指定方式(mongo/redis…)
ACL名詞及其主要方法
roles 角色
- removeRole
- addRoleParents
- allow
- removeAllow
resources 資源
- whatResources
- removeResource
permissions 權(quán)限
users 用戶(hù)
- allowedPermissions
- isAllowed
- addUserRoles
- removeUserRoles
- userRoles
- roleUsers
- hasRole
- areAnyRolesAllowed
使用方法
- 建立起配置文件
- 用戶(hù)登錄后分配相應(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', // 一般用戶(hù) allows: [ { resources: ['/admin/reserve'], permissions: ['get'] }, ] }, { roles: 'member', // 會(huì)員 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自己提供的中間件太雞肋了,這里就重寫(xiě)了一個(gè)。
function auth() { return async function (req, res, next) { let resource = req.baseUrl; if (req.route) { // 正常在control中使用有route屬性 但是使用app.use則不會(huì)有 resource = resource + req.route.path; } console.log('resource', resource); // 容錯(cuò) 如果訪(fǎng)問(wèn)的是 /admin/sign/ 后面為 /符號(hào)認(rèn)定也為過(guò) 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: '用戶(hù)未授權(quán)訪(fǎng)問(wèn)', // }; // return res.status(401).send(err.message); // } next(); }; }
有點(diǎn)要說(shuō)明的是express.Router支持導(dǎo)出一個(gè)Router模塊 再在app.use使用,但是如果你這樣使用 app.use('/admin/user',auth(), userRoute); 那么是在auth這個(gè)函數(shù)是獲取不到 req.route 這個(gè)屬性的。 因?yàn)閍cl對(duì)訪(fǎng)問(wèn)權(quán)限做的是強(qiáng)匹配,所以需要有一定的容錯(cuò)
登錄的權(quán)限分配
result為數(shù)據(jù)庫(kù)查詢(xún)出來(lái)的用戶(hù)信息,或者后臺(tái)api返給的用戶(hù)信息,這里的switch可以使用配置文件的形式,因?yàn)槲疫@邊本次項(xiàng)目只有三個(gè)權(quán)限,所以就在這里簡(jiǎn)單寫(xiě)了一下。
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頁(yè)面中的渲染邏輯控制
在 express+pug中 app.locals.auth= async function(){} 這個(gè)寫(xiě)法在pug渲染的時(shí)候是不會(huì)得出最終結(jié)果的,因?yàn)閜ug是同步的,那么我如何控制當(dāng)前頁(yè)面或者說(shuō)當(dāng)前頁(yè)面的按鈕用戶(hù)是否有權(quán)限展示出來(lái), 這里通用的做法有
- 用戶(hù)在登錄的時(shí)候有一個(gè)路由表和組件表 然后在渲染的時(shí)候 根據(jù)這個(gè)表去渲染
- 在需要權(quán)限控制的地方,使用函數(shù)來(lái)判斷用戶(hù)是否有權(quán)限訪(fǎng)問(wèn)
我這里采用的是結(jié)局方案2.因?yàn)楸容^方便, 但是問(wèn)題來(lái)了 express+pug是不支持異步的寫(xiě)法,而acl提供給我們的全是異步的, 因?yàn)闀r(shí)間原因,我沒(méi)有去深究里面的判斷,而是采用了一種耦合性比較高但是比較方便的判斷方法.
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ù)組第一個(gè)為單純的get路由 isFind = currentPath.includes(path); if (isFind) { // 如果找到包含該路徑 并且method也對(duì)應(yīng)得上 那么則通過(guò) if (i.permissions.includes(method)) { break; } // 如果找到該路徑 但是method對(duì)應(yīng)不上 則繼續(xù)找. continue; } } return isFind; };
上述代碼頁(yè)比較簡(jiǎn)單, 去遍歷acl_conf,查找用戶(hù)是否有當(dāng)前頁(yè)面的或者按鈕的權(quán)限 因?yàn)閍cl_conf在加載的時(shí)候就已經(jīng)被寫(xiě)入內(nèi)存了,所以性能消耗不會(huì)特別大。比如下面的例子。
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這個(gè)組件可以快速打造一個(gè)用戶(hù)的權(quán)限管理模塊。 但是還有個(gè)問(wèn)題 也急速那個(gè)app.locals.hasRole函數(shù), 如果你使用removeAllow動(dòng)態(tài)改變了用戶(hù)的權(quán)限表,那么hasRole函數(shù)就很麻煩了。 所以在這種情況下 有以下幾個(gè)解決方案
- 從acl源碼入手
- 每次渲染的時(shí)候就把數(shù)據(jù)準(zhǔn)備好
const hasBtn1Role = hasRole(user.role, '/xxx','get'); res.render('a.pug',{hasBtn1Role})
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
node.JS事件機(jī)制與events事件模塊的使用方法詳解
本文將詳細(xì)介紹nodeJS事件機(jī)制與events事件模塊的使用方2020-02-02webstorm中配置nodejs環(huán)境及npm的實(shí)例
今天小編就為大家分享一篇webstorm中配置nodejs環(huán)境及npm的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05node.js實(shí)現(xiàn)為PDF添加水印的示例代碼
這篇文章主要介紹了node.js實(shí)現(xiàn)為PDF添加水印的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12如何降低node版本,怎樣實(shí)現(xiàn)降低node版本
這篇文章主要介紹了如何降低node版本,怎樣降低node版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07