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

要实现登录模块,首先需要一个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)}
}

相关内容

热门资讯

中证A500ETF摩根(560... 8月22日,截止午间收盘,中证A500ETF摩根(560530)涨1.19%,报1.106元,成交额...
A500ETF易方达(1593... 8月22日,截止午间收盘,A500ETF易方达(159361)涨1.28%,报1.104元,成交额1...
何小鹏斥资约2.5亿港元增持小... 每经记者|孙磊    每经编辑|裴健如 8月21日晚间,小鹏汽车发布公告称,公司联...
中证500ETF基金(1593... 8月22日,截止午间收盘,中证500ETF基金(159337)涨0.94%,报1.509元,成交额2...
中证A500ETF华安(159... 8月22日,截止午间收盘,中证A500ETF华安(159359)涨1.15%,报1.139元,成交额...
科创AIETF(588790)... 8月22日,截止午间收盘,科创AIETF(588790)涨4.83%,报0.760元,成交额6.98...
创业板50ETF嘉实(1593... 8月22日,截止午间收盘,创业板50ETF嘉实(159373)涨2.61%,报1.296元,成交额1...
港股异动丨航空股大幅走低 中国... 港股航空股大幅下跌,其中,中国国航跌近7%表现最弱,中国东方航空跌近5%,中国南方航空跌超3%,美兰...
电网设备ETF(159326)... 8月22日,截止午间收盘,电网设备ETF(159326)跌0.25%,报1.198元,成交额409....
红利ETF国企(530880)... 8月22日,截止午间收盘,红利ETF国企(530880)跌0.67%,报1.034元,成交额29.0...