我有路线 '/' 和 '/auth'。逻辑是未登录的用户只能访问 /auth 页面,而登录的用户无法访问它。
所以,我做了两个守卫,由于某种原因,我在它们之间得到了无限循环。
我已经设置了这样的路线:
app.routes.ts:
{
path: 'auth',
canActivate: [OnlyUnauthorizedGuard],
children: [
{ path: '', component: AuthComponent, pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
]
},
{
path: '',
data: {
allowedRoleIDs: [UserRole.Customer, UserRole.Owner, UserRole.Admin, UserRole.Root]
},
canActivate: [RoleGuard],
component: MainComponent,
},
我的守卫是:
角色.guard.ts:
export class RoleGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
console.log('Triggered RoleGuard');
const allowedRoles = route.data['allowedRoleIDs'] as number[];
return this.authService.user$.pipe(
map(user => {
console.log('user', user);
if (user === null) {
console.log('Redirecting to /auth since user is null');
this.router.navigate(['/auth']);
return false;
}
if (user && !allowedRoles.includes(user.roleID)) {
console.log('Redirecting to /404');
this.router.navigate(['/404']);
return false;
}
return true;
})
);
}
}`
onlyUnauthorized.guard.ts:
export class OnlyUnauthorizedGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): Observable<boolean> {
console.log('Triggered OnlyUnauthorizedGuard');
return this.authService.user$.pipe(
take(1),
map(user => {
console.log('user ', user);
if (user) {
console.log('User is logged in so redirecting to /');
this.router.navigate(['/']);
return false;
}
console.log('Allowed');
return true;
})
);
}
}
`
如何获取用户数据:
auth.service.ts:
export class AuthService {
private _userSubject = new BehaviorSubject<User | null>(null);
user$ = this._userSubject.asObservable();
constructor(
private http: HttpClient
) {
this.loadUser();
}
private loadUser() {
this._loadingSubject.next(true);
this.http.get<User>('/auth/getUser').pipe(
tap(user => {
this._userSubject.next(user)
console.log(user)
}),
catchError(() => {
this._userSubject.next(null);
return of(null);
}),
finalize(() => this._loadingSubject.next(false))
).subscribe();
}
get user() {
return this._userSubject.value;
}
}`
在页面上我遇到了奇怪的错误:
PM [vite] Internal server error: Page /auth/getUser did not render in 30 seconds.
我在浏览器的网络选项卡上没有看到对 /auth/getUser 的请求。
在 Docker 中访问 /auth 页面时有这样的日志(浏览器中的控制台选项卡上没有日志):
`
2024-10-28 00:26:06 Triggered RoleGuard
2024-10-28 00:26:06 user null
2024-10-28 00:26:06 Redirecting to /auth since user is null
2024-10-28 00:26:06 Triggered OnlyUnauthorizedGuard
2024-10-28 00:26:06 user null
2024-10-28 00:26:06 Allowed
2024-10-28 00:26:06 Triggered RoleGuard
...`
似乎由于某种原因,即使OnlyUnauthorizedGuard返回true,也会发生重定向到/的情况。
如果用户为null,并且访问根路径/,RoleGuard会重定向到/auth,然后一旦允许,它会尝试再次重定向回/,从而导致无限循环。
调整防护装置,防止形成环路。在两个守卫中使用 take(1) 以确保它们正确完成可观察链,然后确保在触发守卫之前加载用户。考虑将用户状态初始化为未定义,并在解析为 null 或用户对象后处理逻辑,并确保不直接在防护内部使用导航。相反,返回一个 URL 树。
角色卫士
export class RoleGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | UrlTree {
console.log('Triggered RoleGuard');
const allowedRoles = route.data['allowedRoleIDs'] as number[];
return this.authService.user$.pipe(
take(1),
map(user => {
console.log('user', user);
if (user === null) {
return this.router.createUrlTree(['/auth']);
}
if (user && !allowedRoles.includes(user.roleID)) {
return this.router.createUrlTree(['/404']);
}
return true;
})
);
}
}
仅限未经授权的Guard
export class OnlyUnauthorizedGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): Observable<boolean> | UrlTree {
console.log('Triggered OnlyUnauthorizedGuard');
return this.authService.user$.pipe(
take(1),
map(user => {
console.log('user ', user);
if (user) {
return this.router.createUrlTree(['/']);
}
return true;
})
);
}
}
尝试添加加载状态以在获取用户数据时处理 UI 反馈。