Guards
Starting from v3.6.0, Midway provides guard capability.
The guard determines whether a given request is handled by the routing handler based on certain conditions that appear at runtime (such as permissions, roles, access control lists, etc.).
In ordinary applications, these logics are usually processed in the middleware, but the logic of the middleware is too common, and it cannot be combined with routing methods gracefully. For this reason, we have designed guards after the middleware and before entering the routing method, which can facilitate method authentication and other processing.
For the following code, we will take @midwayjs/koa as an example.
Write guards
In general, you can write a guard in the src/guard folder.
Create a src/guard/auth.guard.ts to verify whether the route can be accessed by the user.
➜ my_midway_app tree
.
├── src
│ ├── controller
│ │ ├── user.controller.ts
│ │ └── home.controller.ts
│ ├── interface.ts
│ ├── guard
│ │ └── auth.guard.ts
│ └── service
│ └── user.service.ts
├── test
├── package.json
└── tsconfig.json
Midway uses the @Guard decorator to identify the guard. The sample code is as follows.
import { IMiddleware, Guard, IGuard } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Guard()
export class AuthGuard implements IGuard<Context> {
async canActivate(context: Context, suppilerClz, methodName: string): Promise<boolean> {
// ...
}
}
canActivate method is used to verify whether subsequent methods can be accessed in the request. When true is returned, subsequent methods will be executed. When false is canActivate, 403 error codes will be thrown.
Use guards
Guards can be applied to different frameworks. In http, they can be applied globally, to Controllers, and to methods. In other Framework implementations, they can only be used on methods.
Routing guard
After writing the guard, we need to apply it to each controller route.
Using UseGuard decorators, we can apply them to classes and methods.
import { Controller } from '@midwayjs/core';
import { AuthGuard } from '../guard/auth.guard';
@UseGuard(AuthGuard)
@Controller('/')
export class HomeController {
}
Apply guards on methods.
import { Controller, Get } from '@midwayjs/core';
import { ReportMiddleware } from '../middleware/report.middlweare';
import { AuthGuard } from '../guard/auth.guard';
@Controller('/')
export class HomeController {
@UseGuard(AuthGuard)
@Get('/', { middleware: [ ReportMiddleware ]})
async home() {
}
}
You can also pass in arrays.
@UseGuard([AuthGuard, Auth2Guard])
Global guard
We need to join the guard list of the current framework before the application starts. useGuard method, we can add the guard to the guard list.
// src/configuration.ts
import { App, Configuration } from '@midwayjs/core';
import * as koa from '@midwayjs/koa';
import { AuthGuard } from './guard/auth.guard';
@Configuration({
imports: [koa]
// ...
})
export class MainConfiguration {
@App()
app: koa.Application;
async onReady() {
this.app.useGuard(AuthGuard);
}
}
In the same way, multiple guards can be added.
async onReady() {
this.app.useGuard([AuthGuard, Auth2Guard]);
}
Custom error
By default, when the guard's canActivate method returns false, the framework throws a 403 error (ForbiddenError ).
You can also decide on your own in the guard the errors that need to be thrown.
import { IMiddleware, Guard, IGuard, httpError } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Guard()
export class AuthGuard implements IGuard<Context> {
async canActivate(context: Context, suppilerClz, methodName: string): Promise<boolean> {
// ...
if (methodName ==='xxx') {
throw new httpError.ForbiddenError();
}
return true;
}
}
Note that the global error handler will also intercept errors thrown by guards.