Как вы можете использовать canActivate Angular, чтобы отменить результат охраны?
Из угловой документации по canActivate
кажется, вы можете использовать только 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!
5 ответов
Интересная вещь в вашем вопросе - формулировка:
Можно ли как-то сказать: "переходите к этому маршруту только в том случае, если класс 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
учебный класс
У меня была аналогичная проблема - я хотел сделать страницу входа, которая была бы доступна только в том случае, если она не аутентифицирована, а панель инструментов доступна только в том случае, если вы аутентифицированы (и автоматически перенаправляете пользователя на соответствующий). Я решил это, сделав саму стражу логином + маршрут чувствительным:
Маршруты:
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;
}
}
}
}
Думая о вашей проблеме, одним из решений может быть реализация защиты маршрута, которая выполняет логику в обратном порядке.
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
}
}
Я добавил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
}
}
Поместите данные в охранники
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: 'home',
loadChildren: () => import('./modules/home/home.module').then(m => m.HomeModule),
canActivate: [isAuthenticatedGuard],
},
{
path: 'login',
loadComponent: () => import('./modules/login/login.component').then(m => m.LoginComponent),
canActivate: [isAuthenticatedGuard],
data: { disableAccess: true }
},
{
path: 'register',
loadComponent: () => import('./modules/register/register.component').then(m => m.RegisterComponent),
canActivate: [isAuthenticatedGuard],
data: { disableAccess: true }
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const url: string = state.url;
const result = this.checkLogin(url);
const { disableAccess = false } = route.data;
const redirectMessage =
"User is not logged - This routing guard prevents redirection to any routes that needs logging."; // TODO: Move it out
if (result) {
if (disableAccess) {
console.log(redirectMessage);
this.router.navigate(['home/main',]); //TODO: redirect to logout, and prepare application for logout
}
} else {
console.log(redirectMessage);
if (!disableAccess) {
this.router.navigate(['login',]); //TODO: redirect to logout, and prepare application for logout
}
}
return disableAccess ? !result : result;
}
А затем просто манипулируйте им вот так или любым другим способом.
Во-первых: решение создать новую защиту и повторно использовать canActivate из противоположной защиты явно плохое. Могут быть редиректы, которые могут сработать от другого охранника, и вообще идея внедрить Guard другому охраннику - нехорошо.
Буду рад вашим комментариям, если у вас есть слова для меня.