Architecture
Permission Model
Deep Understanding of NextJS Base RBAC Permission System
๐ฏ Overview
NextJS Base adopts the RBAC (Role-Based Access Control) permission model, managing user access permissions through roles.
Core Concepts
| Concept | Description | Example |
|---|---|---|
| User | System User | Admin, Editor, Viewer |
| Role | Role, Collection of Permissions | admin, editor, viewer |
| Permission | Permission, Executable Operation | Create User, Edit Article |
| Menu | Menu, Page Access Entry | User Management, Article Management |
Relationship Diagram
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ RBAC Model โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโ
โ User โ
โ User โ
โโโโโโโโฌโโโโโโโ
โ
โ roles: String[]
โ (Role ID Array)
โผ
โโโโโโโโโโโโโโโ
โ Role โ
โ Role โ
โโโโโโโโฌโโโโโโโ
โ
โโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ
โ โ โ
โ permission โ menu โ inheritMenuPermissions
โ String[] โ String[] โ Boolean
โผ โผ โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ Permission โ โ Menu โโโโโโโโโ
โ Permission โ โ Menu โ
โโโโโโโโโโโโโโโ โโโโโโโโฌโโโโโโโ
โ
โ permission: String[]
โ (Menu Associated Permissions)
โผ
โโโโโโโโโโโโโโโ
โ Permission โ
โ Permission โ
โโโโโโโโโโโโโโโ๐ Model Design
User Model
model User {
id String @id @default(cuid())
email String @unique
name String?
// Role Association
roles String[] @default([]) // Role ID Array
// Admin Access Permission
hasBackendAccess Boolean @default(false)
// Other Fields...
}Role Model
model Role {
id String @id @default(cuid())
name String @unique // Role Name
remark String? // Remarks
enable Boolean @default(true) // Enabled
// Permission Association
permission String[] @default([]) // Permission ID Array
// Menu Association
menu String[] @default([]) // Menu ID Array
inheritMenuPermissions Boolean @default(true) // Inherit Menu Permissions
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Permission Model
model Permission {
id String @id @default(cuid())
name String // Permission Name
parentId String? // Parent ID (Supports Tree)
// Category and Level
crudCategory String? // CRUD Category
level Int @default(0) // Level
// Associated Actions
actions String[] @default([]) // Server Action Name Array
// Associated APIs
apis String[] @default([]) // API Path Array
// Sort and Status
sort Int @default(0)
enable Boolean @default(true)
remark String?
// Soft Delete
deletedAt DateTime?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Menu Model
model Menu {
id String @id @default(cuid())
name String // Menu Name
parentId String? // Parent ID (Supports Tree)
// Route Configuration
url String? // Page Path
icon String? // Icon
// Associated Permissions
permission String[] @default([]) // Associated Permission ID Array
// Display Control
sort Int @default(0)
enable Boolean @default(true)
hidden Boolean @default(false) // Hidden
remark String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}๐ Permission Check
Action Permission Check Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Call Server Action โ
โ sysGetRoleListAction() โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 1: Parse Action Name โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ actionName: 'sysGetRoleList' โ โ
โ โ โ โ
โ โ Parse Result: โ โ
โ โ - prefix: 'sys' โ SYSTEM Level โ โ
โ โ - action: 'Get' โ โ
โ โ - resource: 'RoleList' โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 2: Authentication Check โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ const session = await auth() โ โ
โ โ โ โ
โ โ if (!session?.user) { โ โ
โ โ throw new Error('Please login first') โ โ
โ โ } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 3: Admin Access Check โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ // Check if user has admin access permission โ โ
โ โ if (!user.hasBackendAccess) { โ โ
โ โ throw new Error('No admin access permission') โ โ
โ โ } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 4: RBAC Permission Check โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ // 1. Get all user roles โ โ
โ โ const roles = await getRolesByIds(user.roles) โ โ
โ โ โ โ
โ โ // 2. Aggregate all permission IDs โ โ
โ โ let permissionIds = roles.flatMap(r => r.permission) โ โ
โ โ โ โ
โ โ // 3. If role has menu permission inheritance enabled โ โ
โ โ for (const role of roles) { โ โ
โ โ if (role.inheritMenuPermissions) { โ โ
โ โ const menus = await getMenusByIds(role.menu) โ โ
โ โ permissionIds.push(...menus.flatMap(m => m.permission)) โ โ
โ โ } โ โ
โ โ } โ โ
โ โ โ โ
โ โ // 4. Get permission details โ โ
โ โ const permissions = await getPermissionsByIds(permissionIds)โ โ
โ โ โ โ
โ โ // 5. Aggregate all allowed Actions โ โ
โ โ const allowedActions = permissions.flatMap(p => p.actions) โ โ
โ โ โ โ
โ โ // 6. Check if current Action is in allowed list โ โ
โ โ if (!allowedActions.includes('sysGetRoleList')) { โ โ
โ โ throw new Error('No permission to execute this operation')โ โ
โ โ } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Step 5: Execute Business Logic โ
โ โ
Permission Check Passed โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโMenu Permission Check
Menu permissions are used to control sidebar display in the admin panel:
// Get user accessible menus
async function getUserMenus(userId) {
// 1. Get user
const user = await getUser(userId)
// 2. Get user roles
const roles = await getRolesByIds(user.roles)
// 3. Aggregate all menu IDs
const menuIds = [...new Set(roles.flatMap(r => r.menu))]
// 4. Get menu details
const menus = await getMenusByIds(menuIds)
// 5. Filter enabled and non-hidden menus
const visibleMenus = menus.filter(m => m.enable && !m.hidden)
// 6. Build menu tree
return buildMenuTree(visibleMenus)
}๐จ Permission Configuration Example
Example: Article Management Permissions
1. Create Permissions
// Article Management - Parent Permission
{
name: 'Article Management',
parentId: null,
crudCategory: 'post',
level: 0,
actions: [],
apis: [],
}
// Article Management - View
{
name: 'View Article',
parentId: 'Article Management ID',
crudCategory: 'post',
level: 1,
actions: ['sysGetPostList', 'sysGetPostDetail'],
apis: [],
}
// Article Management - Create
{
name: 'Create Article',
parentId: 'Article Management ID',
crudCategory: 'post',
level: 1,
actions: ['sysCreatePost'],
apis: [],
}
// Article Management - Edit
{
name: 'Edit Article',
parentId: 'Article Management ID',
crudCategory: 'post',
level: 1,
actions: ['sysUpdatePost'],
apis: [],
}
// Article Management - Delete
{
name: 'Delete Article',
parentId: 'Article Management ID',
crudCategory: 'post',
level: 1,
actions: ['sysDeletePost', 'sysBatchDeletePost'],
apis: [],
}2. Create Menu
{
name: 'Article Management',
parentId: 'Content Management ID',
url: '/admin/content/posts',
icon: 'FileTextOutlined',
permission: ['View Article ID', 'Create Article ID', 'Edit Article ID', 'Delete Article ID'],
sort: 10,
enable: true,
}3. Assign to Role
// Editor Role - Only View and Edit Permissions
{
name: 'editor',
permission: ['View Article ID', 'Edit Article ID'],
menu: ['Article Management Menu ID'],
inheritMenuPermissions: false, // Don't inherit menu permissions, use explicit assignment
}
// Admin Role - All Permissions
{
name: 'admin',
permission: [], // Can be empty
menu: ['Article Management Menu ID'],
inheritMenuPermissions: true, // Inherit all permissions associated with menu
}๐ Permission Naming Conventions
Action Naming
| Prefix | Level | Description | Example |
|---|---|---|---|
pub | PUBLIC | Public Access | pubGetConfig |
auth | AUTH | Login Required | authGetUserInfo |
sys | SYSTEM | Admin Permission Required | sysGetRoleList |
Complete Naming Format
{prefix}{Action}{Resource}Action
Examples:
- sysGetRoleListAction // Get Role List
- sysCreateRoleAction // Create Role
- sysUpdateRoleAction // Update Role
- sysDeleteRoleAction // Delete Role
- sysBatchDeleteRoleAction // Batch Delete RolePermission Category (crudCategory)
| Category | Description | Associated Actions |
|---|---|---|
role | Role Management | sysGetRoleList, sysCreateRole, ... |
permission | Permission Management | sysGetPermissionList, ... |
menu | Menu Management | sysGetMenuList, ... |
user | User Management | sysGetUserList, ... |
post | Article Management | sysGetPostList, ... |
โ Best Practices
1. Permission Granularity
โ
Recommended: Fine-grained Permissions
- View Role
- Create Role
- Edit Role
- Delete Role
โ Not Recommended: Coarse-grained Permissions
- Role Management (Includes All Operations)2. Use crudCategory
// โ
Recommended: Use crudCategory for Grouping
{
name: 'View Role',
crudCategory: 'role',
actions: ['sysGetRoleList', 'sysGetRoleDetail'],
}
// โ Not Recommended: Don't Use Category
{
name: 'View Role',
actions: ['sysGetRoleList', 'sysGetRoleDetail'],
}3. Menu Permission Inheritance
// โ
Recommended: For admin roles, use menu permission inheritance
{
name: 'admin',
menu: ['All Menu IDs'],
inheritMenuPermissions: true, // Automatically get all permissions associated with menu
}
// โ
Recommended: For regular roles, explicitly assign permissions
{
name: 'editor',
permission: ['Specific Permission IDs'],
menu: ['Edit Related Menu IDs'],
inheritMenuPermissions: false, // Don't inherit, use explicit assignment
}4. Permission Check Location
// โ
Recommended: Automatic check in wrapAction
export const sysGetRoleListAction = wrapAction(
'sysGetRoleList',
async (params) => {
// Permission already checked automatically
return await dao.getList(params)
}
)
// โ Not Recommended: Manual check in business logic
export const getRoleListAction = async (params) => {
// Manually check permission
if (!await checkPermission('sysGetRoleList')) {
throw new Error('No Permission')
}
return await dao.getList(params)
}๐ Related Documentation
Overall Architecture
System architecture overview and role of permissions
Data Flow Design
Request processing flow and permission check nodes
RBAC Configuration Guide
Configuration methods for roles, permissions, and menus
wrapAction API
Execute permission validation at Action layer
Admin Development Path
Complete implementation path from CRUD to permissions/menus