3

背景

最近在跟老师做项目的时候,需要处理这样一种情况: 当访问网站的某一个功能的时候需要先登录(认证)才能访问。

什么是路由守卫

路由守卫是一种机制,用于在导航到某个路由之前或之后执行一些代码。这可以帮助你控制导航的行为,比如检查用户是否有权限访问某个页面,确认用户是否已经登录,最常见的例子就是我们访问淘宝的东西,我们浏览器商品的时候不用进行登陆,而当我们看中某一种商品的需要点击购买的时候,点击购买的时候就需要我们进行登陆。

Kapture 2023-11-20 at 11.37.49.gif

Angular路由守卫:

  1. CanActivate:
    作用: 在导航到某个路由前执行,决定是否允许进入该路由。
    使用场景: 检查用户是否有足够的权限访问该路由,或者是否已经登录。
  2. CanDeactivate:
    作用: 在离开某个路由前执行,决定是否允许离开该路由。
    使用场景: 提示用户保存未保存的更改,或者执行其他清理操作。
  3. Resolve:
    作用: 在路由激活之前,先解析一些数据。
    使用场景: 在加载组件之前,确保所有需要的数据都已加载。
  4. CanLoad:
    作用: 在异步加载模块之前执行,决定是否允许加载该模块。
    使用场景: 检查用户是否有权限加载某个懒加载的模块。
    image.png

守卫返回一个值,以控制路由器的行为:

  • true 导航过程会继续
  • false 导航过程就会终止,且用户留在原地。
  • UrlTree 取消当前导航,并开始导航到所返回的 UrlTree

canActivate:身份验证

新建 admin模块、新建 admin组件

ng generate module admin
ng generate component admin/admin

编辑配置 admin路由信息:

const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

编辑admin-component模版

<h2>admin</h2>
<nav>
  <a>Dashboard</a>
  <a>Manage Crises</a>
  <a>Manage Heroes</a>
</nav>

添加守卫

添加一个login模块来认证用户信息和登陆

ng generate module login

创建守卫:

ng g g auth  //这里我们选择CanActivate

image.png

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }

可以看出:返回值可以是三种类型的布尔值或 UrlTree。

使用只需要在路由配置文件中添加 CanActivate: [AuthGuard]

const Routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard]
  }
];

此时,当守卫返回的时候true,可以访问admin组件
Kapture 2023-11-20 at 11.58.39.gif

添加一个user服务来进行认证管理登陆状态

ng g service user
 每次值更新会通知订阅值
 loginUser$ = new ReplaySubject<boolean>(1);创建一个ReplaySubject,
import {Injectable} from '@angular/core';
import {ReplaySubject} from "rxjs";

@Injectable({
  providedIn: 'root'
})
export class UserService {
  loginUser$ = new ReplaySubject<boolean>(1);

  login(): void {
    this.loginUser$.next(true);
  }

  logout(): void {
    this.loginUser$.next(false);
  }
}

在守卫中使用userService

import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
import {Observable} from 'rxjs';
import {UserService} from "../service/user.service";
import {map} from "rxjs/operators";

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private userService: UserService,
              private router: Router) {
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    return this.userService.loginUser$.pipe(map(v => {
      if (v) {
        return true;
      } else {
        return this.router.createUrlTree(['login'], {queryParams: {'redirectUrl': state.url}});
      }
    }));
  }
}

新建login组件

ng g c login

编辑login-component.html

<p>{{message}}</p>
<p>
  <button type="button" (click)="login()" *ngIf="!isLogin">Login</button>
  <button type="button" (click)="logout()" *ngIf="isLogin">Logout</button>
</p>

编辑login-component.ts

 message: string = ''; 登陆的信息
 isLogin = false;      登陆的状态
 redirectUrl = 'login';登陆成功之后的跳转地址
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, NavigationExtras, Router, RouterModule} from '@angular/router';
import {UserService} from "../../service/user.service";
import {filter} from "rxjs";

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  message: string = '';
  isLogin = false;
  redirectUrl = 'login';

  constructor(public router: Router,
              private route: ActivatedRoute,
              private userService: UserService) {
  }

  ngOnInit(): void {
    this.route.queryParams.pipe(filter((v => v.hasOwnProperty('redirectUrl')))).subscribe((v) => {
      this.redirectUrl = v['redirectUrl'];
    });

    this.userService.loginUser$.subscribe((value) => {
      this.isLogin = value;
      this.message = this.isLogin ? 'login in' : 'login out';
      if (this.isLogin) {
        this.router.navigate([this.redirectUrl]);
      }
    });
  }


  login() {
    this.message = 'Trying to log in ...';
    this.userService.login();
  }

  logout() {
    this.userService.logout();
  }
}

配置login路由信息:

const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'login',
    component: LoginComponent
  },
];

Kapture 2023-11-20 at 20.19.55.gif

修改app-component模版

<div class="wrapper">
  <h1 class="title">Angular Router</h1>
  <nav>
    <a routerLink="/admin" routerLinkActive="active">Admin</a>
    <a routerLink="/login" routerLinkActive="active">Login</a>
  </nav>
  <router-outlet></router-outlet>
</div>

最总效果

Kapture 2023-11-20 at 11.37.49.gif

总结

路由守卫是Angular中一个非常有用的功能,可以帮助我们控制用户在导航过程中的权限和访问限制

参考文章

https://angular.cn/guide/router-tutorial-toh#milestone-5-rout...
https://blog.csdn.net/yanyi24/article/details/115518763


kexb
519 声望18 粉丝