PEAN栈认证应用:初始登录状态为undefined时的Auth Guards配置
PEAN栈认证应用Auth Guard配置问题
现有代码
if-signed-out.guard.ts
import { CanActivateFn } from '@angular/router'; import { AuthService } from '../services/auth/auth.service'; import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; import { Router } from '@angular/router'; export const IfSignedOut: CanActivateFn = (route, state) => { const auth = inject(AuthService); const router = inject(Router); return auth.getSignInStatusObserver().pipe( map((status) => { if (status) { if (status.success === false) { return true; } else { router.navigate(['/dashboard']); return false; } } else { router.navigate(['/dashboard']); return false; } }) ); };
if-signed-in.guard.ts
import { CanActivateFn } from '@angular/router'; import { AuthService } from '../services/auth/auth.service'; import { inject } from '@angular/core'; import { map } from 'rxjs/operators'; import { Router } from '@angular/router'; export const IfSignedIn: CanActivateFn = (route, state) => { const auth = inject(AuthService); const router = inject(Router); return auth.getSignInStatusObserver().pipe( map((status) => { if (status) { if (status.success === true) { return true; } else { router.navigate(['/dashboard']); return false; } } else if (status === undefined) { return true; } else { router.navigate(['/dashboard']); return false; } }) ); };
app-routing.module.ts
const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: 'sign-up', component: SignUpComponent, canActivate: [IfSignedOut] }, { path: 'sign-in', component: SignInComponent, canActivate: [IfSignedOut] }, { path: 'dashboard', component: DashboardComponent }, { path: 'profile', component: MainProfileComponent, canActivate: [IfSignedIn], children: [ { path: 'my-profile', component: MyProfileComponent, canActivate: [IfSignedIn] }, { path: 'edit-profile', component: EditProfileComponent, canActivate: [IfSignedIn] }, { path: 'change-password', component: ChangePasswordComponent, canActivate: [IfSignedIn] }, { path: 'delete-profile', component: DeleteProfileComponent, canActivate: [IfSignedIn] }, ], }, ];
问题描述
原本未登录用户可直接访问需要登录权限的profile组件,排查发现是auth.service.ts中用BehaviorSubject将初始登录状态设为undefined,导致Guard在初始状态下误判。修改if-signed-in.guard.ts中status === undefined的逻辑为跳转至dashboard后,未登录用户确实无法访问profile,但登录用户也被拦截,无法进入profile相关页面。
需求
实现以下访问控制规则:
- 未登录用户
- 允许访问:Sign in、Sign up、Dashboard组件
- 禁止访问:profile组件,访问时自动重定向到Sign in页面
- 已登录用户
- 禁止访问:Sign in、Sign up组件,访问时自动重定向到Dashboard页面
- 允许访问:Dashboard、profile组件
解决方案
1. 修正AuthService的初始状态
首先确保auth.service.ts中的BehaviorSubject初始状态明确,避免使用undefined。建议初始化为未登录状态,并在服务初始化时验证用户实际登录状态:
// auth.service.ts核心代码片段 import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { private signInStatusSubject = new BehaviorSubject<{ success: boolean }>({ success: false }); public getSignInStatusObserver() { return this.signInStatusSubject.asObservable(); } constructor() { // 初始化时验证用户登录状态 const token = localStorage.getItem('authToken'); if (token) { // 可选:调用后端接口验证token有效性 this.validateToken(token).subscribe(valid => { this.signInStatusSubject.next({ success: valid }); }); } else { this.signInStatusSubject.next({ success: false }); } } // 示例:验证token的方法 private validateToken(token: string) { // 这里替换为你的后端验证逻辑 return this.http.post<{ valid: boolean }>('/api/auth/validate', { token }).pipe( map(res => res.valid) ); } }
2. 修正IfSignedOut Guard
调整逻辑,仅允许未登录用户访问sign-in/sign-up页面:
export const IfSignedOut: CanActivateFn = (route, state) => { const auth = inject(AuthService); const router = inject(Router); return auth.getSignInStatusObserver().pipe( map((status) => { if (status.success === false) { return true; } router.navigate(['/dashboard']); return false; }) ); };
3. 修正IfSignedIn Guard
调整逻辑,仅允许已登录用户访问profile页面,未登录用户重定向到sign-in:
export const IfSignedIn: CanActivateFn = (route, state) => { const auth = inject(AuthService); const router = inject(Router); return auth.getSignInStatusObserver().pipe( map((status) => { if (status.success === true) { return true; } router.navigate(['/sign-in']); return false; }) ); };
4. 优化路由配置
父路由的canActivate会自动应用到所有子路由,无需重复配置,简化路由代码:
const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: 'sign-up', component: SignUpComponent, canActivate: [IfSignedOut] }, { path: 'sign-in', component: SignInComponent, canActivate: [IfSignedOut] }, { path: 'dashboard', component: DashboardComponent }, { path: 'profile', component: MainProfileComponent, canActivate: [IfSignedIn], children: [ { path: 'my-profile', component: MyProfileComponent }, { path: 'edit-profile', component: EditProfileComponent }, { path: 'change-password', component: ChangePasswordComponent }, { path: 'delete-profile', component: DeleteProfileComponent }, ], }, ];
内容的提问来源于stack exchange,提问作者Rok Benko
相关产品推荐
相关产品推荐

