You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 06:45:37