иллюстрировать
В: Для чего используется этот инструмент?
A: Пользователи имеют разные права, такие как администраторы, vip, обычные пользователи, каждому пользователю соответствует API доступа, страница отличается
В Nodejs есть два хорошо известных модуля управления разрешениями, один — acl, а другой — rbac.После всестороннего сравнения я, наконец, выбрал acl, когда работал над проектом.
инструкции
- Создайте файл конфигурации
- Назначьте соответствующие разрешения после входа пользователя в систему.
- Используйте 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', // 最高权限
allows: [
{ resources: ['/admin/reserve', '/admin/sign', '/admin/set'], permissions: ['get'] },
]
}
];
Проверить
Вот вычитка, совмещенная с экспрессом... Получается, что промежуточное ПО, предоставляемое самим 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/ 后面为 /符号认定也为过
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: '用户未授权访问',
// };
// return res.status(401).send(err.message);
// }
next();
};
}
Немного поясню, что express.Router поддерживает экспорт модуля Router и его последующее использование в app.use, но если вы используете его так:app.use('/admin/user',auth(), userRoute);
Тогда функция авторизации не может быть полученаreq.route
этого имущества.
Поскольку acl хорошо подходит для разрешений на доступ, он должен иметь определенную отказоустойчивость.
Назначение прав входа
Результатом является информация о пользователе, запрашиваемая из базы данных, или информация о пользователе, возвращаемая фоновым API.Здесь переключатель может быть в виде файла конфигурации.Поскольку у меня есть только три разрешения для этого проекта, я просто написал его здесь.
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
Отображение логического управления на странице мопса
в экспрессе+мопсapp.locals.auth= async function(){}
Этот способ написания не даст окончательного результата при отображении мопса, потому что мопс является синхронным, поэтому как мне управлять текущей страницей или имеет ли пользователь кнопки текущей страницы разрешение на ее отображение, общая практика здесь такова
- Пользователь имеет таблицу маршрутизации и таблицу компонентов при входе в систему, а затем рендерит в соответствии с этой таблицей при рендеринге
- Если требуется контроль разрешений, используйте функцию, чтобы определить, есть ли у пользователя разрешение на доступ
Здесь я использую окончание плана 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; // 目前数组第一个为单纯的get路由
isFind = currentPath.includes(path);
if (isFind) {
// 如果找到包含该路径 并且method也对应得上 那么则通过
if (i.permissions.includes(method)) {
break;
}
// 如果找到该路径 但是method对应不上 则继续找.
continue;
}
}
return isFind;
};
Приведенная выше кодовая страница относительно проста, чтобы пройти через acl_conf, чтобы узнать, есть ли у пользователя разрешение на текущую страницу или кнопку.Поскольку acl_conf записывается в память при загрузке, потребление производительности не будет особенно большим. Возьмем пример ниже.
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 拒绝
конец
Опираясь на компонент acl, можно быстро создать модуль управления правами пользователей. Но есть еще одна проблема: функция app.locals.hasRole, если вы используете removeAllow для динамического изменения таблицы разрешений пользователя, то функция hasRole очень хлопотная. Итак, в этом случае есть следующие решения
- Начните с исходного кода ACL
- Подготавливайте данные каждый раз при рендеринге
const hasBtn1Role = hasRole(user.role, '/xxx','get');
res.render('a.pug',{hasBtn1Role})