如何在Angular中创建访客Guard,配合Laravel Sanctum实现认证控制
解决方案:Laravel Sanctum + Angular 访客与登录用户权限区分
一、后端Laravel配置(处理访客令牌)
因为你的API受auth:sanctum保护,访客需要合法令牌才能访问问题列表接口,这里通过「固定访客用户」生成令牌的方式实现:
- 创建访客用户
先在数据库中添加标记为访客的用户,可通过Seeder实现:
// database/seeders/GuestUserSeeder.php <?php namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; class GuestUserSeeder extends Seeder { public function run() { User::firstOrCreate( ['email' => 'guest@example.com'], [ 'name' => '匿名访客', 'password' => Hash::make(Str::random(32)), // 随机密码,无需访客知晓 'is_guest' => true, // 需先在users表添加is_guest字段(boolean类型) ] ); } }
执行Seeder:php artisan db:seed --class=GuestUserSeeder
- 新增访客令牌接口
开放无需认证的接口,供前端获取访客令牌:
// routes/api.php Route::post('/guest/token', function () { $guestUser = \App\Models\User::where('is_guest', true)->first(); $token = $guestUser->createToken('Anonymous Guest Token')->plainTextToken; return response()->json(['token' => $token]); });
- API路由权限控制
保持问题列表接口受auth:sanctum保护,创建问题接口额外拦截访客:
Route::middleware('auth:sanctum')->group(function () { // 访客和登录用户都能访问的问题列表 Route::get('/questions', [\App\Http\Controllers\QuestionController::class, 'index']); // 仅登录用户能访问的创建问题接口 Route::post('/questions', [\App\Http\Controllers\QuestionController::class, 'store']) ->middleware(function ($request, $next) { if ($request->user()->is_guest) { return response()->json(['message' => '访客无法创建问题'], 403); } return $next($request); }); });
二、前端Angular配置(路由守卫与令牌管理)
基于你已有的登录用户AuthGuard,新增访客守卫并完善令牌逻辑:
- 创建访客守卫
GuestGuard
拦截访客访问创建问题页面,仅允许登录用户进入:
// src/app/guards/guest.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; @Injectable({ providedIn: 'root' }) export class GuestGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} canActivate(): boolean { // 已登录用户允许访问,访客直接跳首页 if (this.authService.isAuthenticatedUser()) { return true; } this.router.navigate(['/']); return false; } }
- 完善
AuthService管理令牌与用户状态
区分登录用户和访客,自动获取访客令牌:
// src/app/services/auth.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { private currentUser$ = new BehaviorSubject<any>(null); constructor(private http: HttpClient) { this.initAuthState(); } private initAuthState(): void { const token = localStorage.getItem('auth_token'); const user = localStorage.getItem('user_info'); if (user) { this.currentUser$.next(JSON.parse(user)); } else if (token) { // 只有令牌,判定为访客 this.currentUser$.next({ isGuest: true }); } else { // 无令牌,自动获取访客令牌 this.fetchGuestToken(); } } fetchGuestToken(): void { this.http.post('/api/guest/token', {}).subscribe((res: any) => { localStorage.setItem('auth_token', res.token); this.currentUser$.next({ isGuest: true }); }); } // 判断是否为登录用户(非访客) isAuthenticatedUser(): boolean { const user = this.currentUser$.value; return user && !user.isGuest; } // 登录逻辑:覆盖访客令牌,存储登录用户信息 login(email: string, password: string): Observable<any> { return this.http.post('/api/login', { email, password }); } handleLoginResponse(res: any): void { localStorage.setItem('auth_token', res.token); localStorage.setItem('user_info', JSON.stringify(res.user)); this.currentUser$.next(res.user); } // 登出逻辑:清除登录信息,重新获取访客令牌 logout(): void { localStorage.removeItem('auth_token'); localStorage.removeItem('user_info'); this.fetchGuestToken(); } }
- 路由配置
给创建问题页面绑定GuestGuard,首页无守卫:
// src/app/app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { CreateQuestionComponent } from './create-question/create-question.component'; import { AuthGuard } from './guards/auth.guard'; import { GuestGuard } from './guards/guest.guard'; const routes: Routes = [ { path: '', component: HomeComponent }, // 仅登录用户可访问创建页面 { path: 'create', component: CreateQuestionComponent, canActivate: [GuestGuard] }, // 其他登录用户专属路由用你已有的AuthGuard { path: 'profile', component: ProfileComponent, canActivate: [AuthGuard] }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
- API请求拦截器
自动给所有API请求带上令牌:
// src/app/interceptors/auth.interceptor.ts import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const token = localStorage.getItem('auth_token'); if (token) { req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next.handle(req); } }
在AppModule中注册拦截器:
// src/app/app.module.ts import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './interceptors/auth.interceptor'; @NgModule({ providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
三、验证逻辑说明
- 访客打开页面时,前端自动请求访客令牌并存储,后续API请求带上该令牌,后端识别为访客用户,允许访问问题列表但拒绝创建操作;
- 登录用户登录后,前端存储登录令牌和用户信息,覆盖访客令牌,此时
GuestGuard会允许其访问创建页面,后端也会识别为非访客用户,允许创建操作; - 登出后,前端清除登录信息,重新获取访客令牌,回到访客状态。
内容的提问来源于stack exchange,提问作者Armando_pagano
相关产品推荐
相关产品推荐

