【nestjs+mongodb】- 全栈- passport策略的用户登录
创始人
2024-06-01 07:52:28

要实现登录模块,首先需要一个auth权限模块去做

auth权限模块

要开发auth用户权限模块

  1. 首先先要有注册用户模块,其中,libs的user模型对应的用户名和密码的定义如下。 其中select: false是为了不向客户端返回密码,尽管我们对其进行散列加密,但还是不要传回客户端尾号。

user.module.ts

作用:主要供auth注册时,需要注入user模型到auth模块上

import { prop, modelOptions, DocumentType } from '@typegoose/typegoose';
import { ApiProperty } from '@nestjs/swagger';
import { hashSync } from 'bcryptjs';export type UserDocument = DocumentType;@modelOptions({schemaOptions: {timestamps: true,},
})
export class User {@ApiProperty({ description: '用户名', example: 'user1' })@prop()username: string;@ApiProperty({ description: '密码', example: 'pass1' })@prop({select: false,get(val) {return val;},set(val) {return val ? hashSync(val) : val;},})password: string;
}

auth.controller.ts

import { Controller, Post, Body, Get, UseGuards, Req } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiProperty, ApiBearerAuth } from '@nestjs/swagger';
import { InjectModel } from 'nestjs-typegoose';
import { User, UserDocument } from '@libs/db/models/user.model';
import { ReturnModelType, DocumentType } from '@typegoose/typegoose';
import { AuthGuard } from '@nestjs/passport';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { JwtService } from '@nestjs/jwt';
import { CurrentUser } from './current-user.decorator';@Controller('auth')
@ApiTags('用户')
export class AuthController {constructor(private jwtService: JwtService,@InjectModel(User) private userModel: ReturnModelType,) {}@Post('register')@ApiOperation({ summary: '注册' })async register(@Body() dto: RegisterDto) {const { username, password } = dto;const user = await this.userModel.create({username,password,});return user;}@Post('login')@ApiOperation({ summary: '登录' })@UseGuards(AuthGuard('local'))async login(@Body() dto: LoginDto, @CurrentUser() user: UserDocument) {return {token: this.jwtService.sign(String(user._id)),};}@Get('user')@ApiOperation({ summary: '获取个人信息' })@UseGuards(AuthGuard('jwt'))@ApiBearerAuth()async user(@CurrentUser() user: UserDocument) {return user;}
}

自定义装饰器

有上面的代码可知,我们需要取到jwt token校验后的user属性,因此我们需要封装对应装饰器去取到,由于该currentUser装饰器的逻辑简单,只需要取对应req的user即可

import { createParamDecorator, ExecutionContext } from "@nestjs/common";
import { Request } from "express";export const CurrentUser = createParamDecorator((data, ctx: ExecutionContext) => {const req: Request = ctx.switchToHttp().getRequest()return req.user
})

passport 策略

校验用户名和密码,通过model.findOne找到对应的name

安装对应的包

pnpm i passport-jwt passport passport-local

local.strategy

import { Strategy, IStrategyOptions } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { InjectModel } from 'nestjs-typegoose';
import { User } from '@libs/db/models/user.model';
import { ReturnModelType } from '@typegoose/typegoose';
import { BadRequestException } from '@nestjs/common';
import {compareSync} from 'bcryptjs'export class LocalStrategy extends PassportStrategy(Strategy, 'local') {constructor(@InjectModel(User) private userModel: ReturnModelType,) {super({usernameField: 'username',passwordField: 'password',} as IStrategyOptions);}async validate(username: string, password: string) {const user = await this.userModel.findOne({ username }).select('+password');if (!user) {throw new BadRequestException('用户名不正确');}if (!compareSync(password, user.password)) {throw new BadRequestException('密码不正确')}return user;}
}

小结

用户使用流程,登录时,会在调用登录接口时,用一个守卫guard去拦截用户信息,通过passport去检验用户是否存在在数据库。

auth模块 控制器

auth模块 用于用户登录,使用到了passport策略去进行校验

import { Controller, Post, Body, Get, UseGuards, Req } from '@nestjs/common';
import {ApiTags,ApiOperation,ApiProperty,ApiBearerAuth,
} from '@nestjs/swagger';
import { InjectModel } from 'nestjs-typegoose';
import { User, UserDocument } from '@libs/db/models/user.model';
import { ReturnModelType, DocumentType } from '@typegoose/typegoose';
import { AuthGuard } from '@nestjs/passport';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { JwtService } from '@nestjs/jwt';
import { CurrentUser } from './current-user.decorator';@Controller('auth')
@ApiTags('用户')
export class AuthController {constructor(private jwtService: JwtService,@InjectModel(User) private userModel: ReturnModelType,) {}@Post('register')@ApiOperation({ summary: '注册' })async register(@Body() dto: RegisterDto) {const { username, password } = dto;const user = await this.userModel.create({username,password,});return user;}@Post('login')@ApiOperation({ summary: '登录' })@UseGuards(AuthGuard('local'))async login(@Body() dto: LoginDto, @CurrentUser() user: UserDocument) {return {token: this.jwtService.sign(String(user._id)),};}@Get('user')@ApiOperation({ summary: '获取个人信息' })@UseGuards(AuthGuard('jwt'))@ApiBearerAuth()async user(@CurrentUser() user: UserDocument) {return user;}
}

libs/common 通用模块

import { Module, Global } from '@nestjs/common';
import { CommonService } from './common.service';
import { ConfigModule } from '@nestjs/config'
import { DbModule } from '@libs/db';
import {JwtModule} from '@nestjs/jwt'@Global()
@Module({imports: [ConfigModule.forRoot({isGlobal: true}),JwtModule.registerAsync({useFactory(){return {secret: process.env.SECRET}}}),DbModule,],providers: [CommonService],exports: [CommonService, JwtModule],
})
export class CommonModule {}

小结

通过passport策略检验成功后,返回一个jwt配合本地秘钥生成token,供用户调用其他接口时使用,因此我们需要一个jwt策略去

jwt.strategy

import { Strategy, StrategyOptions, ExtractJwt } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { InjectModel } from 'nestjs-typegoose';
import { User } from '@libs/db/models/user.model';
import { ReturnModelType } from '@typegoose/typegoose';export class JwtStrategy extends PassportStrategy(Strategy) {constructor(@InjectModel(User) private userModel: ReturnModelType,) {super({jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),secretOrKey: process.env.SECRET} as StrategyOptions);}async validate(id) {return await this.userModel.findById(id)}
}

相关内容

热门资讯

放弃内燃机禁令后,欧洲与美国仍...   炒股就看金麒麟分析师研报,权威,专业,及时,全面,助您挖掘潜力主题机会! (来源:观察者网) ...
对话灵犀智能程振华:无法高频触... 来源:钛媒体图片来源:网络灵犀智能的创始人程振华,或许是我们栏目对话过所有AI Founder中“最...
法国公共债务超3.48万亿欧元... 转自:北京日报客户端当地时间19日,根据法国国家统计和经济研究所公布的数据,截至2025年第三季度,...
四川成渝:全资子公司拟以24.... 每经AI快讯,12月19日,四川成渝(601107.SH)公告称,全资子公司蜀南公司拟以现金收购蜀道...
奶粉桶内藏黄金——城固公安成功... “多亏你们及时拦截,不然我这5万块钱就打水漂了!”12月19日,一名群众紧握着民警的手连连道谢。当天...