【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)}
}

相关内容

热门资讯

防震演练进校园 筑牢安全“防护... (来源:廊坊日报)转自:廊坊日报 本报讯(记者 曹明明)近日,霸州市煎茶铺镇第一小学组织开展...
港股科技ETF国泰(51302... 4月8日,国泰中证港股通科技ETF(513020)收盘涨4.71%,成交额3.15亿元。港股科技ET...
信息安全ETF嘉实(15961... 4月8日,嘉实中证信息安全主题ETF(159613)收盘涨6.72%,成交额450.41万元。信息安...
港股互联网ETF华宝(5137... 4月8日,华宝中证港股通互联网ETF(513770)收盘涨6.30%,成交额10.05亿元。港股互联...
寒武纪股价连续3天上涨累计涨幅... 4月8日,寒武纪涨4.02%,截至发稿,报1164.00元/股,成交121.36亿元,换手率2.51...