权限认证解决方案——JWT(实践篇)

1,100 阅读1分钟

环境:Nest.js、Passport.js、MongoDB

需要注意的地方记录如下

1 注册

用户注册时,密码需要散列,不能直接将明文密码保存进数据库,而在查询用户时,不需要将用户密码查询出来返回前端。

使用bcrypt进行散列

完整的用户模型如下

import { prop, modelOptions } from '@typegoose/typegoose'
import { ApiProperty } from '@nestjs/swagger'
import { hashSync } from 'bcryptjs'

@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
}

2 登录

登录时服务端进行用户名和密码的校验,并返回token

使用Passport的local策略进行校验

import { Strategy, IStrategyOptions } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport'
import { StrategyOptions } from 'passport-jwt';
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<typeof User>
    ){
        super({
            usernameField: 'username',
            passwordField: 'password'
        } as IStrategyOptions)
    }

    async validate(username: string, password: string){
        const user = await this.userModel.findOne({username: username}).select('+password');
        if(!user) {
            throw new BadRequestException('用户名不正确');
        }
        if(!compareSync(password,user.password)){
            throw new BadRequestException('密码不正确');
        }
        return user;
    }
}

登录成功之后返回token

    @Post('login')
    @ApiOperation({summary: '登录'})
    @UseGuards(AuthGuard('local'))
    async login(@Body() dto: LoginDto, @CurrentUser() user: DocumentType<User>){
        return {
            token: this.jwtService.sign(String(user._id))
        };
    }

3 用户验证

使用Passport的jwt策略进行验证

第一步:取出token

第二步:根据取出的id查找对应的用户

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';
import { BadRequestException } from '@nestjs/common';
import { compareSync } from 'bcryptjs'
 
export class JwtStrategy extends PassportStrategy(Strategy,'jwt'){
    constructor(
        @InjectModel(User) private userModel: ReturnModelType<typeof User>
    ){
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: process.env.SECRET
        } as StrategyOptions)
    }

    async validate(id){
      return await this.userModel.findById(id)
    }
}