如何用Laravel中间件保护Angular中的管理员专属路由?
这个问题在前后端分离项目里太普遍了,前端路由没法直接套用Laravel的中间件,但我们可以结合「前端路由守卫+后端权限校验」的方式来实现,给你分享几个实用的方案:
方案一:JWT + Angular路由守卫(最常用)
这个方案适合JWT作为身份验证方式的场景,核心思路是把用户的权限信息(比如是否是管理员)塞进JWT里,前端通过解析JWT做路由拦截,同时后端API再做一层兜底校验。
1. Laravel端:在JWT中携带权限信息
如果你用的是tymon/jwt-auth包,可以在User模型里自定义JWT的payload字段,把管理员标识加进去:
// app/Models/User.php public function getJWTCustomClaims() { return [ 'is_admin' => $this->is_admin, // 假设你的用户表有is_admin字段 'user_id' => $this->id ]; }
登录接口返回JWT后,前端把token存在localStorage或者sessionStorage里。
2. Angular端:创建管理员专属路由守卫
生成一个AdminGuard守卫,在进入受保护路由前解析JWT,判断用户是否是管理员:
// src/app/guards/admin.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router'; import { JwtHelperService } from '@auth0/angular-jwt'; @Injectable({ providedIn: 'root' }) export class AdminGuard implements CanActivate { constructor(private jwtHelper: JwtHelperService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot): boolean { const token = localStorage.getItem('auth_token'); // 先判断token是否过期或不存在 if (!token || this.jwtHelper.isTokenExpired(token)) { this.router.navigate(['/login']); return false; } // 解析token里的管理员标识 const decodedToken = this.jwtHelper.decodeToken(token); if (!decodedToken.is_admin) { this.router.navigate(['/403']); // 跳转到无权限页面 return false; } return true; } }
然后在路由配置中给需要保护的页面绑定这个守卫:
// src/app/app-routing.module.ts const routes: Routes = [ // 其他路由... { path: 'posts/create', component: PostCreateComponent, canActivate: [AdminGuard] }, { path: 'posts/edit/:id', component: PostEditComponent, canActivate: [AdminGuard] } ];
3. 关键:后端API必须加权限校验
前端守卫只是防君子不防小人,一定要在Laravel的API接口层再加一层校验。比如创建一个AdminMiddleware:
// app/Http/Middleware/AdminMiddleware.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class AdminMiddleware { public function handle(Request $request, Closure $next) { if (!auth()->user() || !auth()->user()->is_admin) { return response()->json(['message' => '无权限执行此操作'], 403); } return $next($request); } }
在app/Http/Kernel.php里注册这个中间件:
protected $routeMiddleware = [ // 其他中间件... 'admin' => \App\Http\Middleware\AdminMiddleware::class, ];
最后给需要保护的API接口绑定这个中间件:
// routes/api.php Route::post('/posts', [PostController::class, 'store'])->middleware('auth:api', 'admin'); Route::put('/posts/{id}', [PostController::class, 'update'])->middleware('auth:api', 'admin');
方案二:后端返回权限列表,Angular动态生成路由
如果你的权限规则更复杂(比如不同角色有不同的路由权限),可以让Laravel返回当前用户的可访问权限列表,Angular根据这个列表动态生成路由。
1. Laravel端:提供权限查询接口
// routes/api.php Route::get('/user/permissions', function () { $user = auth()->user(); $permissions = []; if ($user->is_admin) { $permissions = ['posts.create', 'posts.edit', 'posts.delete']; } return response()->json(['permissions' => $permissions]); })->middleware('auth:api');
2. Angular端:动态加载路由
在应用初始化时调用权限接口,根据返回的权限添加路由:
// src/app/app.component.ts import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { AuthService } from './services/auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { constructor(private router: Router, private authService: AuthService) {} ngOnInit(): void { this.authService.getUserPermissions().subscribe(permissions => { const currentRoutes = this.router.config; // 根据权限添加路由 if (permissions.includes('posts.create')) { currentRoutes.push({ path: 'posts/create', component: () => import('./posts/post-create/post-create.component').then(m => m.PostCreateComponent) }); } if (permissions.includes('posts.edit')) { currentRoutes.push({ path: 'posts/edit/:id', component: () => import('./posts/post-edit/post-edit.component').then(m => m.PostEditComponent) }); } // 更新路由配置 this.router.resetConfig(currentRoutes); }); } }
同样,后端API的权限校验不能少。
方案三:Laravel Sanctum + 后端校验接口(适合同域SPA)
如果你的Angular项目和Laravel后端是同域的,用Sanctum会更省心,它基于Cookie做身份验证,不需要手动处理JWT。
1. Laravel端:配置Sanctum并创建校验接口
先配置好Sanctum的SPA支持,然后创建一个接口专门用来校验用户是否是管理员:
// routes/api.php Route::get('/check-admin', function () { return response()->json(['is_admin' => auth()->user()->is_admin]); })->middleware('auth:sanctum', 'admin');
2. Angular端:路由守卫调用后端校验接口
在守卫里直接请求这个接口,根据返回结果判断是否允许访问:
// src/app/guards/admin.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { map, catchError } from 'rxjs/operators'; import { of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AdminGuard implements CanActivate { constructor(private http: HttpClient, private router: Router) {} canActivate() { return this.http.get('/api/check-admin').pipe( map(() => true), catchError(() => { this.router.navigate(['/403']); return of(false); }) ); } }
核心原则
不管用哪个方案,一定要记住:前端路由守卫只是优化用户体验,真正的权限控制必须在后端API层实现,因为前端的任何限制都可以被开发者工具绕过。
内容的提问来源于stack exchange,提问作者Hamid

