В настоящее время из-за переноса фреймворка Nodejs исходный серверный проект typerx был перенесен в фреймворк NestJS, и часть управления разрешениями была завершена, и я хотел бы поделиться им с вами. адрес проекта:
Введение в официальное управление персонажем NestJs
Поскольку эта статья в основном представляет собой введение в часть управления разрешениями, поэтому предварительно у вас уже есть представление об идентификации пользователя.Если вы хотите узнать о входе пользователя в систему, обратитесь к другим связанным документам.
-
GuardsОхранники — это аннотированные охранники, описывающие ограничения доступа декорированного контроллера. Он должен реализовать интерфейс CanActivate. Охранники несут единственную ответственность за принятие решения о том, может ли запрос быть маршрутизирован или нет. *** Стоит отметить, что Guards идут после каждого промежуточного ПО, но до перехватчиков и каналов. **
-
Прежде чем понять разрешения, нам нужно понять два понятия, одно — Аутентификация, а другое — Авторизация.Вы не запутались.Да, они очень похожи, но на самом деле они разные.
Аутентификация — это в основном проверка личности, что означает, например, вопрос о том, есть ли у вас удостоверение личности (вы вошли в систему) -> 401
Авторизация в основном связана с идентификацией роли, что означает запрос на то, является ли учетная запись вашей идентификационной карты локальной (какая роль, есть ли у вас разрешение) -> 403.
(Являются ли официальные документы Защита авторизации # и аутентификация на основе ролей # немного перевернутыми?) auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true;
}
}
С AuthGuard и RolesGuard наш контроллер можно написать так, конечно, вы можете связать несколько, разделенных запятыми.
RolesGuard с внедрением зависимостей
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}
Вместо внедрения зависимостей вы также можете использовать новый метод для создания экземпляра для него.
@Controller('cats')
@UseGuards(new RolesGuard())
export class CatsController {}
Глобально можно сохранить каждое место для аннотирования, но на шлюзах и микросервисах это не сработает (проверяется)
const app = await NestFactory.create(ApplicationModule);
app.useGlobalGuards(new RolesGuard());
- После описанных выше действий у нас уже есть RolesGuard, но нам также нужно связать роль с контроллером.
Посмотрите прямо на написаниеcats.controller.ts
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Таким образом, роль связана с контроллером, и мы можем ограничить доступ к этому интерфейсу только администраторами, но код немного уродлив и не очень лаконичен, верно? Тогда поступим иначе:
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Это намного лучше? Это главное, что мы можем добавить следующий декоратор
roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
Детали реализации roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<string[]>('roles', context.getHandler()); // 从控制器注解中得到的角色组信息。
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasRole = () => user.roles.some((role) => roles.includes(role)); // 是否匹配到角色
return user && user.roles && hasRole();
}
}
Здесь в основном нужно получить пользователя из контекста и вынуть из пользователя аннотации роли и роли в контроллере, чтобы посмотреть, есть ли совпадение пересечения, и если да, то отпустить.
В общем, официально предоставленных документов не так много, и, таким образом, все решение представляет собой лишь простую защиту ролей, и оно не может дополнить сложную систему разрешений, но также может удовлетворить общую негибкую систему конфигурации.
Требования к системе разрешений Nestx
- Роли могут быть определены сами по себе. Графика:
-
В меню есть различные типы узлов разрешений, такие как: управление чтением и записью и т. д. Графика:
-
Роли можно настроить в узле «Конфигурация разрешений» в меню «Администратор». Схема та же, что и выше.
Справочные ресурсы
nest-access-control
import { Get, Controller, UseGuards } from '@nestjs/common';
import { UserRoles, UseRoles, ACGuard } from 'nest-access-control';
import { AppService } from './app.service';
import { AuthGuard } from './auth.guard';
import { AppRoles } from 'app.roles';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@UseGuards(AuthGuard, ACGuard)
@UseRoles({
resource: 'video',
action: 'read',
possession: 'any',
})
@Get()
root(@UserRoles() userRoles: any) {
return this.appService.root(userRoles);
}
}
Метод аннотирования Guard более интересен, он разделен на ресурсы, поведения и разрешения, кажется, что этот метод аннотирования больше подходит для наших нужд.
Существует класс RolesBuilder, который может создавать определения:
// app.roles.ts
export enum AppRoles {
USER_CREATE_ANY_VIDEO = 'USER_CREATE_ANY_VIDEO',
ADMIN_UPDATE_OWN_VIDEO = 'ADMIN_UPDATE_OWN_VIDEO',
}
export const roles: RolesBuilder = new RolesBuilder();
roles
.grant(AppRoles.USER_CREATE_ANY_VIDEO) // define new or modify existing role. also takes an array.
.createOwn('video') // equivalent to .createOwn('video', ['*'])
.deleteOwn('video')
.readAny('video')
.grant(AppRoles.ADMIN_UPDATE_OWN_VIDEO) // switch to another role without breaking the chain
.extend(AppRoles.USER_CREATE_ANY_VIDEO) // inherit role capabilities. also takes an array
.updateAny('video', ['title']) // explicitly defined attributes
.deleteAny('video');
То, что мы видим до сих пор, в основном хранится в виде файлов, нам нужно поместить его в базу данных, на этот модуль можно ссылаться для последующего анализа.
node-casbin
Casbin кажется относительно популярным модулем разрешений, доступным на разных языках и предоставляющим более полные функции.
- Поддержка пользовательского формата запроса, формат запроса по умолчанию: {тема, объект, действие};
- Он имеет две основные концепции модели модели управления доступом и политики политики;
- Поддержка многоуровневого наследования ролей в RBAC, роли могут быть не только у субъектов, но и у ресурсов;
- Поддержка суперпользователей, таких как root или Administrator, суперпользователи могут получать доступ к произвольным ресурсам без ограничений политиками авторизации;
- Поддержка различных встроенных операторов, таких как keyMatch, для облегчения управления ресурсами на основе пути, например, /foo/bar может быть сопоставлен с /foo*;
Текущие примеры интеграции вnesjs для node-casbin: nest-casbin и nt-casbin, но эти два модуля относительно просты и обеспечивают только простые вызовы сервисных пакетов.
enforcer.enforce(sub, obj, act);
И нет тега аннотации пакета. Вышеизложенное является некоторой подготовкой и анализом понимания, и конкретная реализация будет продолжена.