如何使用 Angular 的 canActivate 来否定守卫的结果?

问题描述 投票:0回答:5

canActivate

 上的 Angular 文档
来看,如果
canActivate
函数最终返回
canActivate
,则似乎只能使用
true
守卫来允许继续执行路线。

有没有办法说“只有在

canActivate
类评估为
false
时才继续这条路线”?

例如,为了不允许登录用户访问登录页面,我尝试了这个,但没有成功:

export const routes: Route[] = [
    { path: 'log-in', component: LoginComponent, canActivate: [ !UserLoggedInGuard ] },

我在控制台中收到此错误:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError[false]: 
  StaticInjectorError[false]: 
    NullInjectorError: No provider for false!
Error: StaticInjectorError[false]: 
  StaticInjectorError[false]: 
    NullInjectorError: No provider for false!
angular typescript angular-routing canactivate angular-guards
5个回答
24
投票

你的问题中有趣的是它的表述:

有没有什么办法可以说“只有在以下情况下才继续走这条路线” canActivate 类的计算结果为 false”?

以及你如何表达“直观”的解决方案:

{ path: 'log-in', component: LoginComponent, canActivate: [ !UserLoggedInGuard ] },

基本上就是说,您需要

negate
UserLoggedInGuard@canActivate

的结果

让我们考虑以下

UserLoggedInGuard
的实现:

@Injectable()
export class UserLoggedInGuard implements CanActivate {
   constructor(private _authService: AuthService) {}

   canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this._authService.isLoggedIn();
    }
} 

接下来我们看看@Mike提出的解决方案

@Injectable()
export class NegateUserLoggedInGuard implements CanActivate {    
    constructor(private _authService: AuthService) {}

   canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return !this._authService.isLoggedIn();
    }
}

现在,该方法没问题,但与

UserLoggedInGuard
的(内部)实现紧密耦合。如果由于某种原因
UserLoggedInGuard@canActivate
的实现发生变化,
NegateUserLoggedInGuard
将中断。

我们怎样才能避免这种情况呢?简单,滥用依赖注入:

@Injectable()
export class NegateUserLoggedInGuard implements CanActivate {    
  constructor(private _userLoggedInGuard: UserLoggedInGuard) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
     return !this._userLoggedInGuard.canActivate(route,state);
  }
}

现在这正在做完全你所表达的

canActivate: [ !UserLoggedInGuard ]

最好的部分:

  • 它与
    UserLoggedInGuard
  • 的内部实现没有紧密耦合
  • 可以扩展以操作多个
    Guard
    类的结果

3
投票

我遇到了类似的问题 - 想要制作一个只有在未经过身份验证时才可以访问的登录页面,以及只有经过身份验证才可以访问的仪表板(并自动将用户重定向到适当的页面)。我通过让守卫本身对登录+路由敏感来解决这个问题:

路线:

const appRoutes: Routes = [
  { path: 'login', component: LoginComponent, canActivate: [AuthGuard] },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },

守卫:

export class AuthGuard implements CanActivate {

  private login: UrlTree;
  private dash: UrlTree;

  constructor(private authSvc: AuthenticationService, private router: Router ) {
    this.login = this.router.parseUrl('login');
    this.dash = this.router.parseUrl('dashboard');
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
    if (this.authSvc.isSignedIn()) {
      if (route.routeConfig.path === 'login') {
        return this.dash;
      } else {
        return true;
      }
    } else {
      if (route.routeConfig.path === 'login') {
        return true;
      } else {
        return this.login;
      }
    }
  }
}

2
投票

考虑您的问题,一种解决方案可能是实现一个反向执行逻辑的路由防护。

import { MyService } from "./myservice.service";
import { CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot } from "@angular/router";
import { Injectable } from "@angular/core";

@Injectable()
export class MyGuard implements CanActivate {

    constructor(private myService: MyService) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.myService.isNotLoggedIn(); //if user not logged in this is true
    }
}

0
投票

我在所有路线中添加了

data.auth: boolean
选项(除了那些应该可以通过任何方式访问的路线),如下所示:

const routes: Routes = [
  {
    path: '', // all
    component: HomeComponent,
  },
  {
    path: 'authenticate', // not authenticated
    component: AuthComponent,
    data: { auth: false },
    canActivate: [AuthGuard],
  },
  {
    path: 'account', // authenticated
    component: AccountComponent,
    data: { auth: true },
    canActivate: [AuthGuard],
  },
]

然后在我的身份验证防护中,我检查此数据属性并采取相应措施:

export class AuthGuard implements CanActivate {
  constructor(private readonly supabase: SupabaseService) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot,
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return route.data['auth'] ? !!this.supabase.session : !this.supabase.session
  }
}

0
投票

将数据放入守卫中

然后像这样或任何其他方式操纵它

第一:创建新守卫并重用对面守卫的 canActivate 的解决方案显然很糟糕。 可以有可以从另一个守卫触发的重定向,并且一般来说将守卫注入到另一个守卫 - 不好。

如果您有什么话要对我说的话,我很高兴看到您的评论。

© www.soinside.com 2019 - 2024. All rights reserved.