上节末我们提出一个问题:
试想一下,假设用户已经登陆,但仍访问注册页面,是否不合适?
又或者用户没有登陆,但要访问必须登陆者才能访问的页面,怎么办?
按常理来说,我们可以在每个接口开始进行鉴权操作,只是这样就与业务代码深耦合了,有没有更好的办法呢? 当然有。
我们可以使用安全守卫(Guard)来保护接口。
看张摘自nestjs官网的图:
意思是想要访问到路由接口,就必须经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,看到页面自动跳转到登陆页:
登陆后,再访问http://localhost:8000/signin,会跳转到首页:
作业
下一步, 我们将开始开发博客页面,包含增删改查功能。你可以想想怎么开发了。