1

我有一个简单的 Ionic 4/5 选项卡式应用程序(我从他们的选项卡启动器开始)。我想制作一个由身份验证保护的选项卡。这是我想要实现的目标:

  • 为登录用户、配置文件管理、帐户管理等保留选项卡之一。
  • 因此,当用户单击第三个选项卡时,默认页面是由 AuthGuard 保护的“配置文件”,如果 ionic 存储中没有用户,则重定向到登录/注册页面

这是我到目前为止所尝试的:

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  static STORAGE_KEY = 'auth-user';

  user: BehaviorSubject<User> = new BehaviorSubject(null);
  redirectUrl: string;

  constructor(
    @Inject(ROUTES_CONFIG)
    private readonly ROUTES: AppRoutes,
    private readonly router: Router,
    private readonly storage: Storage,
    private readonly platform: Platform,
    private readonly http: HttpClient
  ) {
    this.platform.ready().then(() => {
      this.checkUser();
    });
  }

  async checkUser() {
    const user = await this.storage.get(AuthService.STORAGE_KEY) as User;
    if (user) {
      this.user.next(user);
    }
  }

  login(credentials): Observable<any> {
    const loginObservable = this.http.post(`http://localhost:3000/auth/login`, credentials);

    loginObservable.subscribe(async (user: User) => {
      await this.storage.set(AuthService.STORAGE_KEY, user);
      this.user.next(user);
      this.router.navigateByUrl(this.redirectUrl || this.ROUTES.AUTH);
    });

    return loginObservable;
  }

  async logout(): Promise<void> {
    await this.storage.remove(AuthService.STORAGE_KEY);
    this.user.next(null);
    this.router.navigateByUrl(this.ROUTES.LOGIN);
  }

  isLoggedIn(): Observable<User> {
    return this.user;
  }

}

还有守卫:

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Promise<boolean> | Observable<boolean> | boolean {
    let url: string = state.url;

    // Store the attempted URL for redirecting
    this.authService.redirectUrl = url;

    return this.authService
      .isLoggedIn()
      .pipe(
        skip(1),
        map(user => {
          if (user) {
            console.log('authenticated');
            return true;
          }

          console.error('Not authenticated, redirecting to login');
          this.router.navigateByUrl(this.ROUTES.LOGIN);
          return false;
        })
      )
  }

问题在于,一旦经过身份验证,authguard 就永远不会进入map. 我把它放在skip(1)那里是因为我想跳过初始null值。我怎样才能“推迟”AuthGuard,直到我的 AuthService 检查用户是否存在于存储中,因为毕竟这就是我想要做的。

4

1 回答 1

0

我无法以这种方式解决它,但我可以用另一种方式解决它,方法如下:

以前我有

  • tab1 模块、tab2 模块、tab3 模块(配置文件、登录、注册)
  • 个人资料页面由 AuthGuard 保护。如果未找到重定向到登录的用户
  • AuthService -> 是从 AuthGuard 调用的,帮助用户状态为 BehaviorObject -> 请参阅我的原始问题

这就是我改变的方式

  • tab1 模块,tab2 模块相同
  • tab3 模块(个人资料页面)
  • 登录,注册现在在根模块中
  • 创建了 APP_INITIALIZER 我检查存储中是否有用户
  • 创建了一个新的 UserService 只是为了存储用户
  • AuthService 只管理重定向、登录、注册

相关代码

app.module.ts-> 在提供者中

    BootstrappingService,
    {
      provide: APP_INITIALIZER,
      useFactory: (bootstrappingService: BootstrappingService) =>
        () => bootstrappingService.initApp(),
      deps: [BootstrappingService],
      multi: true
    },

BootstrappingService.ts

@Injectable()
export class BootstrappingService {

  constructor(
    private readonly storage: Storage,
    private readonly platform: Platform,
    private readonly userService: UserService
  ) {}

  async initApp() {
    await this.platform.ready();
    const user: User = await this.storage.get(AuthService.STORAGE_KEY);
    if (user) {
      this.userService.user.next(user);
    }
  }
}

UserService.ts-> 保持认证(用户)状态

@Injectable({
  providedIn: 'root'
})
export class UserService {

  user: BehaviorSubject<User> = new BehaviorSubject(null);

  constructor() { }
}

AuthService.ts

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  static STORAGE_KEY = 'auth-user';

  redirectUrl: string;

  constructor(
    @Inject(ROUTES_CONFIG)
    private readonly ROUTES: AppRoutes,
    private readonly router: Router,
    private readonly storage: Storage,
    private readonly userService: UserService,
    private readonly http: HttpClient
  ) {}

  login(credentials): Observable<any> {
    const loginObservable = this.http.post(`http://localhost:3000/auth/login`, credentials);
    loginObservable.subscribe(async (user: User) => {
      await this.storage.set(AuthService.STORAGE_KEY, user);
      this.userService.user.next(user);
      this.router.navigateByUrl(this.redirectUrl || this.ROUTES.PROFILE);
    });
    return loginObservable;
  }

  async logout(): Promise<void> {
    await this.storage.remove(AuthService.STORAGE_KEY);
    this.userService.user.next(null);
    this.router.navigateByUrl(this.ROUTES.HOME);
  }
}

最后AuthGuard.ts

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(
    @Inject(ROUTES_CONFIG)
    private readonly ROUTES: AppRoutes,
    private readonly userService: UserService,
    private readonly authService: AuthService,
    private readonly router: Router
  ) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Promise<boolean> | Observable<boolean> | boolean {
    let url: string = state.url;

    // Store the attempted URL for redirecting
    this.authService.redirectUrl = url;

    return this.userService.user
      .asObservable()
      .pipe(
        map((user: User) => {
          if (user) {
            console.log('authenticated');
            return true;
          }

          console.error('Not authenticated, redirecting to login', user);
          this.router.navigateByUrl(this.ROUTES.LOGIN);
          return false;
        })
      )
  }
}

现在我受保护的第三个选项卡似乎可以正确处理登录/注销状态。在受保护的路线上点击刷新也可以。

于 2020-02-20T21:02:17.350 回答