Designing RBAC Permission System with NestJS: Eine Schritt-für-Schritt-Anleitung
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Vorwort
Für Backend-Managementsysteme sind Funktionen wie Zugriffskontrolle und personalisierte Benutzeroberflächen unerlässlich. Beispielsweise kann ein Superadministrator alle Seiten anzeigen, normale Benutzer können auf die Seiten A und B zugreifen, und VIP-Benutzer können die Seiten A, B, C und D anzeigen. Die Logik hinter diesen Funktionalitäten basiert auf dem Design von drei Schlüsselkonzepten:
- Benutzer: Die Basiseinheit, wie Alice, Bob, Charlie.
- Rolle: Ein Benutzer kann eine oder mehrere Rollen haben. Zum Beispiel kann Alice sowohl die Rolle eines normalen Benutzers als auch eines VIP haben.
- Berechtigung: Eine Rolle ist mit mehreren Berechtigungen verbunden. Beispielsweise kann die VIP-Rolle Berechtigungen zum Anzeigen, Bearbeiten und Hinzufügen haben, während der Superadministrator anzeigen, bearbeiten, hinzufügen und löschen kann.
Die Beziehung kann anhand des folgenden Diagramms veranschaulicht werden:
Als Nächstes werden wir Nest
verwenden, um das Fundament eines solchen Systems von Grund auf zu implementieren – das Berechtigungsdesign.
Erstellen der Datenbank
Zuerst müssen wir die Datenbank erstellen. Wir verwenden die MySQL
-Datenbank und führen den folgenden Befehl aus, um sie zu erstellen:
CREATE DATABASE `nest-database` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
Projektinitialisierung
Wir starten ein neues Nest
-Projekt, indem wir den folgenden Befehl ausführen:
nest new nest-project
Installieren Sie dann die erforderlichen Datenbankabhängigkeiten, hauptsächlich typeorm
und mysql2
:
npm install --save @nestjs/typeorm typeorm mysql2
Konfigurieren Sie als Nächstes typeorm
in app.module.ts
:
import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'nest-database', synchronize: true, logging: true, entities: [__dirname + '/**/*.entity{.ts,.js}'], poolSize: 10, connectorPackage: 'mysql2', }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
Tabellendesign
Typischerweise hat ein RBAC-System (Role-Based Access Control) die folgenden 5 Tabellen:
- Benutzertabelle (
user
): Speichert grundlegende Benutzerinformationen wie Benutzername, Passwort und E-Mail. - Rolltabelle (
role
): Speichert Rollendetails wie Rollenname und Rollencode. - Berechtigungstabelle (
permission
): Speichert Berechtigungsdetails wie Berechtigungsname und Berechtigungscode. - Benutzer-Rolle-Beziehungstabelle (
user_role_relation
): Verfolgt die Beziehung zwischen Benutzern und Rollen. - Rolle-Berechtigung-Beziehungstabelle (
role_permission_relation
): Verfolgt die Beziehung zwischen Rollen und Berechtigungen.
Das Domänenmodell kann wie folgt visualisiert werden:
Als Nächstes erstellen wir drei Nicht-Beziehungstabellen in Nest
und definieren ihre Beziehungen.
user.entity.ts
:
import { Column, CreateDateColumn, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { Role } from './role.entity'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ length: 50, }) username: string; @Column({ length: 50, }) password: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; @ManyToMany(() => Role) @JoinTable({ name: 'user_role_relation', joinColumn: { name: 'userId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'roleId', referencedColumnName: 'id', }, }) roles: Role[]; }
In der Tabelle User
ist das Feld roles
definiert, um eine Verbindung mit der Tabelle user_role_relation
herzustellen. Die Beziehungslogik lautet: user.id === userRoleRelation.userId
und role.id === userRoleRelation.roleId
. Übereinstimmende Role
-Einträge werden automatisch mit User
verknüpft.
role.entity.ts
:
import { Column, CreateDateColumn, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { Permission } from './permission.entity'; @Entity() export class Role { @PrimaryGeneratedColumn() id: number; @Column({ length: 20, }) name: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; @ManyToMany(() => Permission) @JoinTable({ name: 'role_permission_relation', joinColumn: { name: 'roleId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'permissionId', referencedColumnName: 'id', }, }) permissions: Permission[]; }
Das Feld permissions
in der Tabelle Role funktioniert ähnlich. Es verbindet sich mit der Tabelle role_permission_relation
unter Verwendung der Logik: role.id === rolePermissionRelation.roleId
und permission.id === rolePermissionRelation.permissionId
.
permission.entity.ts
:
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @Entity() export class Permission { @PrimaryGeneratedColumn() id: number; @Column({ length: 50, }) name: string; @Column({ length: 100, nullable: true, }) desc: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; }
Die Tabelle Permission hat keine Beziehungen; sie erfasst lediglich verfügbare Berechtigungen.
Dateninitialisierung
Hier ist ein Dienst zum Initialisieren einiger Testdaten:
async function initData() { const user1 = new User(); user1.username = 'Alice'; user1.password = 'aaaaaa'; const user2 = new User(); user2.username = 'Bob'; user2.password = 'bbbbbb'; const user3 = new User(); user3.username = 'Charlie'; user3.password = 'cccccc'; const role1 = new Role(); role1.name = 'Administrator'; const role2 = new Role(); role2.name = 'Regular User'; const permission1 = new Permission(); permission1.name = 'Add resource_a'; const permission2 = new Permission(); permission2.name = 'Edit resource_a'; const permission3 = new Permission(); permission3.name = 'Delete resource_a'; const permission4 = new Permission(); permission4.name = 'Query resource_a'; const permission5 = new Permission(); permission5.name = 'Add resource_b'; const permission6 = new Permission(); permission6.name = 'Edit resource_b'; const permission7 = new Permission(); permission7.name = 'Delete resource_b'; const permission8 = new Permission(); permission8.name = 'Query resource_b'; role1.permissions = [ permission1, permission2, permission3, permission4, permission5, permission6, permission7, permission8, ]; role2.permissions = [permission1, permission2, permission3, permission4]; user1.roles = [role1]; user2.roles = [role2]; await this.entityManager.save(Permission, [ permission1, permission2, permission3, permission4, permission5, permission6, permission7, permission8, ]); await this.entityManager.save(Role, [role1, role2]); await this.entityManager.save(User, [user1, user2]); }
Führen Sie den Dienst initData
über einen Browser oder Postman aus, und die Daten werden in die Datenbank eingetragen.
Mit der grundlegenden Berechtigungsstruktur können Sie nun Funktionen wie Registrierung, Anmeldung und JWT-basierte Authentifizierung implementieren.
Jetzt sind Sie an der Reihe!
Wir sind Leapcell, Ihre erste Wahl für die Bereitstellung von NestJS-Projekten in der Cloud.
Leapcell ist die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis:
Multi-Sprachen-Unterstützung
- Entwickeln Sie mit JavaScript, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Reaktionszeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Kein Betriebsaufwand – konzentrieren Sie sich einfach auf das Bauen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ