Angular 5/6: защита маршрута (защита маршрута) без перенаправления на маршрут ошибки - PullRequest
0 голосов
/ 23 мая 2018

У меня немного рассола.Я использую Route guard (реализующий интерфейс CanActivate), чтобы проверить, предоставлен ли пользователю доступ к определенному маршруту:

const routes: Routes = [
    {
        path: '',
        component: DashboardViewComponent
    },
    {
        path: 'login',
        component: LoginViewComponent
    },
    {
        path: 'protected/foo',
        component: FooViewComponent,
        data: {allowAccessTo: ['Administrator']},
        canActivate: [RouteGuard]
    },
    {
        path: '**',
        component: ErrorNotFoundViewComponent
    }
];

Теперь он отлично работает для защиты маршрута / protected / foo от активации, ноЯ хотел бы сообщить пользователю, что маршрут, к которому он пытается получить доступ, запрещен (аналогично 403 Запрещено, вы можете получить с сервера).

Проблема: Как показать пользователю это специальное представление об ошибкене перенаправляя его на маршрут ошибки , какие швы являются предпочтительным вариантом для многих источников, которые я нашел? И как мне по-прежнему использовать мой RouteGuard, фактически не загружая запрещенный маршрут, потому что, если я проверяю доступ внутри моего FooViewComponent и отображаю другой вид, это как бы лишает смысла наличие RouteGuard в первомplace.

В идеале я бы хотел, чтобы мой RouteGuard не только возвращал false в методе canActivate(), но и полностью заменял компонент, скажем, ErrorForbiddenViewComponent.Но я понятия не имею, как это сделать, или это возможно событие.Любые альтернативы?

Вот так теперь выглядит мой охранник маршрута:

import {Injectable} from '@angular/core';
import {Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
import {AuthService} from '../services/auth.service';

@Injectable()
export class RouteGuard implements CanActivate {

    constructor(
        private router: Router,
        private auth: AuthService
    ) {}

    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const { auth, router } = this;
        const { allowAccessTo } = next.data;
        const identity = auth.getIdentity();
        if (
            identity &&
            allowAccessTo.indexOf(identity.role)
        ) {
            // all good, proceed with activating route
            return true;
        }
        if (identity) {
            // TODO show ErrorForbiddenViewComponent instead of redirecting
            console.log('403 Forbidden >>', next);
        }
        else { 
            // not logged in: redirect to login page with the return url
            const [returnUrl, returnQueryParams] = state.url.split('?');
            console.log('401 Unauthorised >>', returnUrl, returnQueryParams, next);
            router.navigate(['/login'], {queryParams: {returnUrl, returnQueryParams}});
        }
        return false;
    }
}

Так что я просто запрещаю загрузку маршрута, но не перенаправляю.Я только перенаправляю незарегистрированных посетителей на маршрут входа в систему.

Причина:

  • Маршруты должны отражать определенное состояние приложения - посещение URL-адреса маршрута должно воссоздать это состояние
  • Дляналичие маршрутов ошибок (за исключением 404 Not Found) означало бы, что ваше приложение может фактически воссоздавать состояния ошибок.Это не имеет никакого смысла, так как почему вы сохраняете состояние ошибки как состояние вашего приложения?Для целей отладки следует использовать журналы (консоль или сервер), повторное посещение страницы ошибок (т. Е. Обновление страницы) может помешать этому.
  • Кроме того, путем перенаправления на маршрут ошибки приложение должно предоставить некоторую информацию об ошибке пользователю.В этом случае либо какой-либо параметр должен быть передан через url, либо (что намного хуже) сохранить состояние ошибки в некотором сервисе ошибок и получить его при доступе к маршруту ошибки.
  • Кроме того, игнорирование RouteGuard и просто загрузка компонентаи проверка доступа внутри него может привести к загрузке некоторых дополнительных зависимостей, которые в любом случае не будут использоваться (так как пользователь не разрешен), значительно усложняет загрузку с отложенным доступом.

У кого-нибудь есть какое-то решениеза это?Мне также интересно, почему после того, как Angular 2+ так долго существовал, у кого-то такого раньше не было?Все в порядке с перенаправлением?

Также имейте в виду, что хотя в настоящее время я использую FooViewComponent синхронно, это может измениться в будущем!

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

Я когда-то работал над подобной проблемой.

Совместное использование моего стекалитка POC , где я создал -

  • Аутентифицируемый компонент (с защитой)
  • Компонент входа в систему
  • Permission Guard
  • Маршрут (/auth маршрут предоставляется с PermissionGuardService guard)

Guard оценивает тип пользователяи соответственно обрабатывает перенаправление / ошибку.

Варианты использования: -

  • Пользователь не авторизован (shows a toast with log in message)
  • Пользователь не является администратором (shows a toast with unauthorised message)
  • Пользователь - администратор (show a toast with success messaage)

Я сохранил пользователя в локальном хранилище.

РЕДАКТИРОВАТЬ - ДЕМО enter image description here

Дайте мне знать, если вам нужна специальная обработка, и я обновлю кодовую базу.

Приветствия!

0 голосов
/ 02 июня 2018

Ваш RouteGuard может внедрить любую службу, которую вы используете для модальных окон, а .canActivate() может вывести модал без перенаправления, чтобы информировать пользователя, не нарушая текущего состояния приложения.

Для этого мы используем toastr и его угловую оболочку, поскольку он создает немодальное всплывающее окно, которое самоотключается через столько секунд, кнопки ОК / Отмена не нужны.

0 голосов
/ 30 мая 2018

После просмотра angular2 примера , предоставленного Таруном Лалвани в комментариях к вопросу, и после более глубокого изучения Статьи по загрузке динамических компонентов на Angular docs Мне удалось применить ее к моему коду:

Я больше не использую свой RouteGuard при указании маршрутов:

{
     path: 'protected/foo',
     component: FooViewComponent,
     data: {allowAccessTo: ['Administrator']}, // admin only
     canActivate: [RouteGuard]
},

Вместо этого я создал специальный RouteGuardComponent, и вот как я его использую:

{
    path: 'protected/foo',
    component: RouteGuardComponent,
    data: {component: FooViewComponent, allowAccessTo: ['Administrator']}
},

Это код RouteGuardComponent:

@Component({
    selector: 'app-route-guard',
    template: '<ng-template route-guard-bind-component></ng-template>
    // note the use of special directive ^^
})
export class RouteGuardComponent implements OnInit {

    @ViewChild(RouteGuardBindComponentDirective)
    bindComponent: RouteGuardBindComponentDirective;
    // ^^ and here we bind to that directive instance in template

    constructor(
        private auth: AuthService,
        private route: ActivatedRoute,
        private componentFactoryResolver: ComponentFactoryResolver
    ) {
    }

    ngOnInit() {
        const {auth, route, componentFactoryResolver, bindComponent} = this;
        const {component, allowAccessTo} = route.snapshot.data;
        const identity = auth.getIdentity();
        const hasAccess = identity && allowAccessTo.indexOf(identity.role);
        const componentFactory = componentFactoryResolver.resolveComponentFactory(
            hasAccess ?
               component : // render component
               ErrorForbiddenViewComponent // render Forbidden view
        );
        // finally use factory to create proper component
        routeGuardBindComponentDirective
            .viewContainerRef
            .createComponent(componentFactory);
    }

}

Кроме того, для этого требуется определить специальную директиву (я уверен, что это можно сделать другим способом, но я только что применил этот динамический компонентпример из Angular docs):

@Directive({
    selector: '[route-guard-bind-component]'
})
export class RouteGuardBindComponentDirective {
    constructor(public viewContainerRef: ViewContainerRef) {}
}

Это не полный ответ на мой собственный вопрос (но это начало), поэтому, если кто-то предоставит что-то лучше (то есть способ все еще использовать canActivate и способностьк ленивой нагрузке) Я обязательно учту это.

...