nest.js学习笔记(五) --jwt验证
作者:互联网
1、前期准备工作
a、安装数据库,mysql, 配置数据库连接参看四
b、新建User模块,用于管理User
实现注册:
@Controller('user') export class UserController { public constructor( private readonly userService: UserService, ) {} @Post('/register') public async register(@Body() body: any) { return await this.userService.registerUser(body); } }
userService实现简易版(注意,这里用的是bcrypt来实现对密码进行加密的,具体参看nest官方文档)
@Injectable() export class UserService { public constructor(@InjectRepository(User) private readonly user: Repository<User>) {} public async registerUser(info: { name: string; nickname: string; password: string }) { const { name, nickname, password } = info; const user = await this.user.findOne({ where: { name } }); if (!user) { const saltOrRounds = 10; const pwd = await bcrypt.hash(password, saltOrRounds); await this.user .createQueryBuilder() .insert() .into(User) .values([ { name, nickName: nickname, password: pwd, createTime: Date.now() / 1000, updateTime: Date.now() / 1000, }, ]) .execute(); return { code: 0, message: 'ok', }; } return { code: 1, message: '用户已存在', }; } }
这样就已实现了简易版的注册
2、jwt的配置与验证
a、安装依赖
yarn add @nestjs/passport passport passport-jwt @nestjs/jwt -S yarn add @types/passport-jwt -D
b、创建 Auth 模块
nest g module /routers/auth // 创建auth模块 nest g service /routers/auth // 创建auth服务
c. 新建一个存储常量的文件
在 auth
文件夹下新增一个 constants.ts
,用于存储各种用到的常量:
export const jwtSalt = '.even_jwt_@@__';
d、编写 JWT 策略
在 auth
文件夹下新增一个 jwt.strategy.ts
,用于编写 JWT 的验证策略:
import { ExtractJwt, Strategy } from 'passport-jwt'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable } from '@nestjs/common'; import { jwtSalt } from './constants'; import * as crypto from 'crypto-js'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: jwtSalt, }); } // JWT验证 - Step 4: 被守卫调用 async validate(payload: any) { console.log(`JWT验证 - Step 4: 被守卫调用`); const info = payload.info; // const userInfo = crypto.AES.decrypt(info, 'salt').toString(crypto.enc.Utf8); // console.log(JSON.parse(userInfo)); return { info, }; } }
注意:为了防止jwt被编译出来,所以采用了加密方式, 注释掉的部份只是为了验证,可以不编写,如果有其他需求的话按该方式进行解码
e、编写auth.service.ts的验证逻辑
import { User } from '@app/entities/user.entity'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import * as bcrypt from 'bcrypt'; import { JwtService } from '@nestjs/jwt'; import * as crypto from 'crypto-js'; @Injectable() export class AuthService { public constructor( @InjectRepository(User) private user: Repository<User>, private readonly jwtService: JwtService, ) {} // JWT验证 - Step 2: 校验用户信息 public async validateUser(name: string, pwd: string): Promise<boolean> { const user = await this.user.findOne({ select: ['name', 'password'], where: { name, }, }); return user && bcrypt.compare(pwd, user.password); } // JWT验证 - Step 3: 处理 jwt 签证 public certificate(user: any): string { // 这里对jwt的内容采用了 crypto 中的aes的对称加密方法 const payload = { info: crypto.AES.encrypt( JSON.stringify({ name: user.name, password: user.password }), 'salt', ).toString(), }; return this.jwtService.sign(payload); } }
f、关联jwt策略与service到Auth.module模块中
import { JwtStrategy } from './jwt.strategy'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Module } from '@nestjs/common'; import { PassportModule } from '@nestjs/passport'; import { JwtModule } from '@nestjs/jwt'; import { AuthService } from './auth.service'; import { User } from '@app/entities/user.entity'; import { jwtSalt } from './constants'; @Module({ imports: [ TypeOrmModule.forFeature([User]), PassportModule.register({ defaultStrategy: 'jwt' }), JwtModule.register({ secret: jwtSalt, signOptions: { expiresIn: '30m' }, // token 过期时效 }), ], providers: [AuthService, JwtStrategy], exports: [AuthService], // 因为auth模块需要导入到User模块,所以需要配置导出 }) export class AuthModule {}
g、在user模块中需要导入AuthModule, 因为user模块需要用到签发证书以及用户验证的方法
import { TypeOrmModule } from '@nestjs/typeorm'; import { Module } from '@nestjs/common'; import { UserService } from './user.service'; import { UserController } from './user.controller'; import { User } from '@app/entities/user.entity'; import { AuthModule } from '../auth/auth.module'; @Module({ imports: [TypeOrmModule.forFeature([User]), AuthModule], providers: [UserService], controllers: [UserController], exports: [UserService], }) export class UserModule {}
h、在UserController中添加登录的方法
import { AuthService } from './../auth/auth.service'; import { UserService } from './user.service'; import { Body, Controller, Post } from '@nestjs/common'; @Controller('user') export class UserController { public constructor( private readonly userService: UserService, private readonly authService: AuthService, ) {} @Post('/register') public async register(@Body() body: any) { return await this.userService.registerUser(body); } @Post('/login') public async login(@Body() body: any) { const { name, nickname, password } = body; const result = await this.authService.validateUser(name, password); if (result) { return { code: 0, message: 'ok', token: this.authService.certificate({ name, password }), // 签发token }; } return { code: 1, message: 'fail', }; } }
i、在指定路径上添加jwt验证
@UseGuards(AuthGuard('jwt')) // 使用 'JWT' 进行验证 @Get() public async init() { const result = await this.cryptoService.cryptoPassword(); return { code: 0, token: result, }; }
这个时候访问已需要验证的路由,那么就需要在header中添加 字段 Authorization
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZvIjoiVTJGc2RHVmtYMStpVW1NMXBzWm13b3YyUS9QSHBTY3lqMzBTOVFpb1pDZW这里也说一下 JWT 的缺点,主要是无法在使用同一账号登录的情况下,后登录的,挤掉先登录的,也就是让先前的 Token 失效,从而保证信息安全, 这里就需要配合redis来实现唯一性登录
xPdHRvSGo2Rm9wZFZXSnFoNko4M3hPb2NsK1QzanNIS0NuaXNRUkVnckE9PSIsImlhdCI6MTY1MzE0OTYwMiwiZXhwIjoxNjUzMTUxNDAyfQ.vp62XIxwdynol4aql7
XgYE4-Xpq1PcpaTUzLbd5UHyU
标签:const,--,nest,jwt,auth,user,import,nestjs 来源: https://www.cnblogs.com/venblogs/p/16296722.html