2.8 安全守卫

74 阅读2分钟

上节末我们提出一个问题:

试想一下,假设用户已经登陆,但仍访问注册页面,是否不合适?

又或者用户没有登陆,但要访问必须登陆者才能访问的页面,怎么办?

按常理来说,我们可以在每个接口开始进行鉴权操作,只是这样就与业务代码深耦合了,有没有更好的办法呢? 当然有。

我们可以使用安全守卫(Guard)来保护接口。

看张摘自nestjs官网的图

image.png

意思是想要访问到路由接口,就必须经Guard同意。我们只需要实现相应的Guard,就可以配合装饰器UseGuards,将非法的接口拒之门外。

代码

guard

新建src/guards/sso.guard.ts,我们实现2个Guard,一个是未登陆,则跳转到登陆页面;一个是已登陆,跳转到首页。

import { Context, Injectable } from "oak_nest";
import type { CanActivate } from "oak_nest";

/**
 * 如果没有登陆,跳转到登陆页面
 */
@Injectable()
export class SSOGuard implements CanActivate {
  canActivate(context: Context) {
    const b = this.validateRequest(context);
    if (!b) {
      context.state.error = "未登陆";
      context.response.redirect("/signin");
    }
    return b;
  }

  validateRequest(context: Context) {
    return context.state.session?.user;
  }
}

/**
 * 如果已经登陆,直接跳转到首页
 */
@Injectable()
export class LoginedGuard implements CanActivate {
  canActivate(context: Context) {
    const b = this.validateRequest(context);
    if (!b) {
      context.state.error = "已登陆";
      context.response.redirect("/posts");
    }
    return b;
  }

  validateRequest(context: Context) {
    return !context.state.session || !context.state.session.user;
  }
}

user.controller.ts

修改src/user/user.controller.ts,给相应的接口加上相应的Guard。提示下,UseGuards可以添加到Controller上,也可以加到具体方法上;参数可以是单个,也可以是多个;执行顺序从外到内,从左到右。

import { LoginedGuard, SSOGuard } from "../guards/sso.guard.ts";

@Controller("/")
export class UserController {
  @Get("/signup")
  @UseGuards(LoginedGuard)
  signupPage() { }

  @Post("signup")
  @UseGuards(LoginedGuard)
  async signup() {}   
  
   @Get("signin")
  @UseGuards(LoginedGuard)
  signinPage() {}

  @Post("signin")
  @UseGuards(LoginedGuard)
  async signin() {}

  @Get("user")
  @UseGuards(SSOGuard)
  async getAllUsers() {}

  @Get("currentUserInfo")
  @UseGuards(SSOGuard)
  currentUserInfo() {}

  @Get("user/:id")
  @UseGuards(SSOGuard)
  async getUserById() { }

  @Delete("user/:id")
  @UseGuards(SSOGuard)
  async deleteUser() {}

  @Get("signout")
  @UseGuards(SSOGuard)
  signout() {}
}

验证

在浏览器访问http://localhost:8000/currentUserInfo,看到页面自动跳转到登陆页:

image.png

登陆后,再访问http://localhost:8000/signin,会跳转到首页:

image.png

作业

下一步, 我们将开始开发博客页面,包含增删改查功能。你可以想想怎么开发了。