XinAdmin implements user authentication and authorization based on Laravel Sanctum. Every interface, menu, page, and button in the application can be considered as a Sanctum ability, and access permissions are controlled through Menu Rules.
Core Features
- Controller Attribute Route Permission Verification - Automatically verifies user permissions through route annotations
- Dynamic Menus - Dynamically generates menus based on user roles
- Page Button Permission Verification - Controls page element display based on user permissions
Database Structure
sys_rule - Rules Table
Stores all system permission rules, including menu, route, and button-level permissions.
type Rule Type Description:
sys_role - Roles Table
sys_role_rule - Role Permission Pivot Table
Permission Type Details
Used for sidebar menu display, corresponding to system menu structure.
// Menu type example
[
'id' => 1,
'parent_id' => 0,
'type' => 'menu',
'name' => 'System Management',
'key' => 'system',
'path' => '/admin/system',
'icon' => 'SettingOutlined',
'order' => 1,
'status' => 0,
'hidden' => 0,
]
Route Type (route)
Corresponds to specific pages or functions, generates actual route rules and participates in permission verification.
// Route type example
[
'id' => 2,
'parent_id' => 1,
'type' => 'route',
'name' => 'User Management',
'key' => 'system.user',
'path' => '/admin/system/user',
'local' => 'menu.system.user',
'order' => 1,
'status' => 0,
'hidden' => 0,
]
Permission Type (rule)
Used for fine-grained permission control, such as button permissions within a page.
// Permission type example
[
'id' => 10,
'parent_id' => 2,
'type' => 'rule',
'name' => 'Export Users',
'key' => 'system.user.export',
'order' => 1,
'status' => 0,
]
Permission Verification Flow
Backend Permission Verification
- When user logs in, the system retrieves all user permission keys through
SysUserService::ruleKeys()
- When user accesses an endpoint,
authorize middleware verifies if the request has corresponding permissions
- The
authorize parameter in route annotations is matched against permission keys
// Controller example
#[RequestAttribute(
routePrefix: '/admin/user',
abilitiesPrefix: 'admin'
)]
class UserController extends Controller
{
// Required permission: admin.user.create
#[PostRoute('/create', 'user.create')]
public function create(): JsonResponse { }
// Required permission: admin.user.update
#[PutRoute('/{id}', 'user.update')]
public function update(int $id): JsonResponse { }
}
Super Administrator
The account with user ID 1 is the super administrator and has all permissions.
// Logic in SysUserService
if ($id == 1) {
// Super administrator has all permissions
return SysRuleModel::query()->where('status', 1)->pluck('key')->toArray();
}
Backend Implementation
After user logs in, call /system/user/menu endpoint to get menu data:
// SysUserController::menu()
public function menu(): JsonResponse
{
$id = Auth::id();
$menus = $this->service->getAdminMenus($id);
return $this->success(compact('menus'));
}
public function getAdminMenus(int $id): array
{
// Super administrator gets all menus
if ($id == 1) {
$menus = SysRuleModel::query()
->where('status', 1)
->whereIn('type', ['menu', 'route'])
->get()
->toArray();
} else {
// Regular users get menus through roles
$roles = SysUserModel::with(['roles.rules' => function ($query) {
$query->where('status', 1)
->whereIn('type', ['menu', 'route']);
}])->find($id)->roles;
$menus = collect($roles)
->map(fn ($item) => $item['rules'])
->collapse()
->unique('id')
->toArray();
}
return $this->getTreeData($menus);
}
Frontend manages menu state through useMenuStore:
// Get menus
const menus = await menu();
Menu rendering filters hidden items:
const transformMenus = (nodes: IMenus[], t: any): MenuItem[] => {
return nodes.reduce<MenuItem[]>((acc, node) => {
// Only process route and menu types that are not hidden
if (!['route', 'menu'].includes(node.type!) || !node.hidden) {
return acc;
}
// Build menu item...
}, []);
};
Permission Middleware
abilities Middleware
Verifies if user has specific permission ability:
// Usage
$middleware[] = 'abilities:' . $authorize;
// Example: abilities:admin.user.create
authGuard Middleware
Verifies if user is authenticated:
$middleware[] = 'auth:sanctum';
$middleware[] = 'authGuard'; // Or with parameter authGuard:admin
Management Page Features
- Menu List: Tree display of all menu rules
- Add Rule: Supports adding menu, route, and permission types
- Edit Rule: Modify rule information
- Delete Rule: Delete rule (requires confirmation)
- Set Display Status: Control whether menu is displayed in sidebar
- Set Enable Status: Control whether rule is active
Add Rule Examples
Add Top-level Menu:
Parent Menu: Top-level Menu
Type: Menu
Name: Content Management
Unique Key: content
Sort: 10
Add Sub-level Route:
Parent Menu: Content Management
Type: Route
Name: Article Management
Unique Key: content.article
Route Path: /admin/content/article
i18n: menu.content.article
Sort: 1
Add Button Permission:
Parent Menu: Article Management
Type: Permission
Name: Audit Article
Unique Key: content.article.audit
Sort: 1
Relationship with Attribute Routing
Menu rules work with attribute routing. Permission key composition rules:
The key field in menu rules must match the permission key generated by attribute routing:
// Controller
#[RequestAttribute(
routePrefix: '/admin/user',
abilitiesPrefix: 'admin'
)]
class UserController extends Controller
{
#[PostRoute('/create', 'user.create')]
public function create() { }
// Permission: admin.user.create
}
// Corresponding rule in menu rules table
['key' => 'admin.user.create', 'name' => 'Create User']
Best Practices
1. Permission Key Naming Convention
Recommended module.resource.operation naming:
system.user.list - User List
system.user.create - Create User
system.user.update - Update User
system.user.delete - Delete User
system.user.export - Export User
2. Role Permission Assignment
Recommended to create multiple roles, each with different permission combinations:
- Super Administrator: Has all permissions
- Operations Administrator: Content management related permissions
- Regular Administrator: Basic view permissions
For sensitive operations within a page (such as delete, audit), use rule type permission:
// React component showing button based on permission
{hasPermission('content.article.audit') && (
<Button onClick={handleAudit}>Audit</Button>
)}