一、守卫
目标
部分接口需要用户登录后才可以访问,用户登录后可以颁发 jwt_token 给前端,前端在调用需要鉴权的接口,需要在请求头添加 jwt_token,后端校验通过才能继续访问,否则返回403无权访问
创建守卫 anth
安装依赖
npm i @nestjs/jwt -S
配置 jwt 常量参数 constants/index.ts
// jwt秘钥,固定随机字符串
export const JWT_SECRET = "NODE_TEST_SECRET";
// jwt有效期 Eg: "60s", "3h", "2d"
export const JWT_EXPIRES = "2h"; // 2小时
创建 auth 模块 auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JWT_SECRET, JWT_EXPIRES } from '../constants';
@Module({
imports: [
JwtModule.register({
secret: JWT_SECRET, // jwt秘钥
signOptions: {
expiresIn: JWT_EXPIRES, // jwt有效期
}
})
],
providers: [AuthService],
exports: [AuthService]
})
export class AuthModule {}
创建 auth 服务 auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
// 创建jwt_token
async createToken(userInfo) {
// 将用户信息(userId、name)存放到 jwt 中
return {
access_token: this.jwtService.sign(userInfo)
};
}
// 校验登录状态
validateToken(token) {
if (!token) {
return false;
}
try {
return this.jwtService.verify(token);
} catch {
return false;
}
}
}
创建 auth 守卫 auth/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization']; // 从请求头中获取 token
if (!token) {
return false;
} else {
return this.authService.validateToken(token); // 如果 token 有效,返回 true,允许访问资源
}
}
}
使用守卫 auth
需要鉴权的模块添加 auth 模块
api.module.ts
...
import { AuthModule } from '../auth/auth.module';
@Module({
imports: [AuthModule],
...
})
export class ApiModule {}
需要鉴权的接口引入守卫
api.controller.ts
...
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../auth/auth.guard';
@Controller('api')
export class ApiController {
constructor(private readonly apiService: ApiService) {}
@Get('getUserInfo')
@UseGuards(AuthGuard) // 需要鉴权的接口添加UseGuards
getUserInfo(): any {
return this.apiService.getUserInfo();
}
}
登录
调用 auth/auth.service.ts 中的 createToken 生成 jwt_token 返回给前端
前端在请求头添加 authorization
如果请求头没有 authorization,或者 authorization 失效,则返回 403 Forbidden
二、全局守卫
目标
由于单个守卫需要引入守卫模块、守卫方法,并且针对每一个接口加 @UseGuards 装饰器,使用起来比较繁琐。项目中经常遇到非常多的接口都需要鉴权,所以就需要使用全局守卫来校验需要鉴权的接口。
注意:获取与解析 jwt_token 的方法上面守卫的方法一致,auth/auth.service,此处不再赘述。
创建全局守卫
配置需要鉴权的接口常量 constants/index.ts
// 需要校验 jwt 的 url 列表
export const ValidUrlList = [
'/api/getUserInfo',
'/api/product/buy',
'/api/queryUserWallet'
];
新建全局守卫 global.guard.ts
import { Injectable, NestInterceptor, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from './auth/auth.service';
import { ValidUrlList } from './constants';
@Injectable()
export class GlobalGuard implements NestInterceptor {
constructor(private authService: AuthService) {}
intercept(context: ExecutionContext, next): Observable<any> {
// 在这里执行全局守卫逻辑
const request = context.switchToHttp().getRequest();
// 从请求头中获取 token
const token = request.headers['authorization'];
// 请求的url,用于判断是否需要校验jwt
const url = request.url;
if (ValidUrlList.includes(url)) {
// 需要鉴权才能访问的接口
const validRes = this.authService.validateToken(token);
if (!validRes) {
return next.handle().pipe(map(() => {
// 如果没有提供令牌,返回错误响应或执行其他逻辑
return new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}));
}
}
// 不需要校验的接口、校验通过的接口直接放行
return next.handle();
}
}
在 app.module.ts 配置全局守卫
// ...
import { APP_INTERCEPTOR } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { GlobalGuard } from './global.guard';
import { JWT_SECRET, JWT_EXPIRES } from './constants';
import { AuthService } from './auth/auth.service';
@Module({
imports: [
JwtModule.register({
secret: JWT_SECRET, // jwt秘钥
signOptions: {
expiresIn: JWT_EXPIRES, // jwt有效期
}
}),
],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: GlobalGuard,
},
AuthService
],
})
export class AppModule {}