Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Защитники маршрутов

Защитник (guard) — функция, которую маршрутизатор вызывает перед переходом на страницу. Она решает: пустить пользователя, не пускать или отправить на другую страницу. Так закрывают от гостей, например, форму новой статьи.

Сервис авторизации

Защитнику нужно знать, вошёл ли пользователь. Сделаем простой сервис без настоящего сервера:

ng generate service auth
// src/app/auth.ts
import { Injectable, computed, signal } from '@angular/core';
 
@Injectable({ providedIn: 'root' })
export class Auth {
  readonly token = signal<string | null>(null);
  readonly isLoggedIn = computed(() => this.token() !== null);
 
  login() {
    this.token.set('demo-token');
  }
 
  logout() {
    this.token.set(null);
  }
}

В настоящем приложении login() отправил бы логин и пароль на сервер и сохранил полученный токен.

Создание защитника

ng generate guard auth

CLI спросит, какой тип защитника создать, — выберите CanActivate. Появится файл src/app/auth-guard.ts (в версиях до 20 — auth.guard.ts):

// src/app/auth-guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { Auth } from './auth';
 
export const authGuard: CanActivateFn = () => {
  const auth = inject(Auth);
  const router = inject(Router);
 
  if (auth.isLoggedIn()) {
    return true;
  }
  return router.createUrlTree(['/login']);
};

Если функция вернёт true, переход состоится. Если вернёт адрес, созданный createUrlTree(), маршрутизатор перенаправит туда. false просто отменит переход.

Подключение к маршруту

Защитник указывают в массиве canActivate:

// src/app/app.routes.ts
export const routes: Routes = [
  { path: 'login', component: Login },
  { path: 'posts', component: PostList },
  { path: 'posts/new', component: PostForm, canActivate: [authGuard] },
  { path: 'posts/:id', component: PostDetail },
];

Откройте http://localhost:4200/posts/new — вместо формы откроется /login. Маршрут posts/new стоит выше posts/:id, иначе слово new было бы принято за номер статьи.

Страница входа

ng generate component login
// src/app/login/login.ts
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
import { Auth } from '../auth';
 
@Component({
  selector: 'app-login',
  template: `<button (click)="enter()">Войти как Анна</button>`,
})
export class Login {
  private auth = inject(Auth);
  private router = inject(Router);
 
  enter() {
    this.auth.login();
    this.router.navigate(['/posts/new']);
  }
}

Нажмите «Войти как Анна» — защитник пропустит, и откроется форма новой статьи.

Другие виды защитников

Свойство маршрутаКогда срабатывает
canActivateПеред входом на страницу
canActivateChildПеред входом на любую дочернюю страницу
canDeactivateПеред уходом со страницы (например, есть несохранённая форма)
canMatchПри выборе маршрута: если false, проверяется следующий

Частая ошибка

Считать защитника защитой данных. Защитник работает в браузере, и любой пользователь может обойти его через инструменты разработчика. Он только улучшает удобство: не показывает лишние страницы. Настоящую проверку прав всегда делает сервер при каждом запросе.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.