nestjs使用jwt完成注册、登录、全局身份验证

最近也是在学习nestjs这一块的内容,也是看到nestjs安全这一块内容,顺便就使用jwt去实现了一下注册、登录、用户密码加密和对后端接口做一个统一的权限处理,注册登录功能大家都很熟悉,而接口权限统一处理,也就是咱们平时开发时,除了登录注册等一些公共接口以外的接口,需要对token进行一个验证,验证不通过则返回401,通过才去返回正确的状态码和数据。下面是整体的一个流程,可能有点长,我会一步一步带大家去完成这些功能,并附上源码,希望大家耐心看完。

首先,咱们去创建一个nestjs项目,使用
nest new xxx,我这里简单取名为auth,包管理工具我这里就用pnpm了,大家可以根据自己电脑上安装的包管理工具去选择,选择好回车一下去安装,安装时间可能有点长,大家耐心等待一下
在这里插入图片描述
安装完成后,使用nest g res auth去生成restful风格的auth模块,下面是具体操作
在这里插入图片描述
在这里插入图片描述

然后我们找到auth.controllert.tsauth.service.ts文件,把这些方法先去除掉,移除完成后长下面这样

在这里插入图片描述
在这里插入图片描述
由于咱们要去注册用户,肯定是需要一张用户的表,接着咱们先去连接数据库,我这里使用mysql数据库,Nest提供了与现成的 TypeORM与@nestjs/typeorm的紧密集成,我这里也是使用TypeORM,对于数据库的安装,网上也是有很多教程,我这里就不具体去讲了。
咱们先去安装这三个包

pnpm install --save @nestjs/typeorm typeorm mysql2

然后在app.module.ts文件去配置连接mysql

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [AuthModule, TypeOrmModule.forRoot({
    type: 'mysql', // 数据库类型
    host: 'localhost', // 主机名
    port: 3306, // 端口
    username: 'root', // 用户名
    password: '123456', // 密码
    database: 'nestjs', // 数据库名称
    synchronize: true,
    retryDelay: 500, //重试连接数据库间隔
    retryAttempts: 10,//重试连接数据库的次数
    autoLoadEntities: true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
  }),],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

接着在auth.entity.ts文件中去写关于用户表的配置,我这里就做三个字段id,username,password

import { Column, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class NV_Users {

    // id为主键并且自动递增
    @PrimaryGeneratedColumn()
    id:number

    @Column()
    username:string

    @Column()
    password:string
}

然后使用TypeOrmModule.forFeature()方法注册该表,这样我们可以使用@InjectRepository()装饰器将NV_UsersRepository注入到auth.service.ts中。

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NV_Users } from './entities/auth.entity';


@Module({
  imports: [TypeOrmModule.forFeature([NV_Users])],
  controllers: [AuthController],
  providers: [AuthService]
})
export class AuthModule { }

注入NV_UsersRepositoryauth.service.ts中。

import { Injectable } from '@nestjs/common';
import { CreateAuthDto } from './dto/create-auth.dto';
import { UpdateAuthDto } from './dto/update-auth.dto';
import { NV_Users } from './entities/auth.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';


@Injectable()
export class AuthService {
    constructor(@InjectRepository(NV_Users) private readonly user:Repository<NV_Users>){}
}

接着在auth.controller.ts去写对应的方法,去调用auth.service.ts中对应的方法。
下面是auth.controller.ts文件代码

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateAuthDto } from './dto/create-auth.dto';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) { }


  // 注册
  @Post("/signup")
  signup(@Body() signupData: CreateAuthDto) {
    return this.authService.signup(signupData)
  }

  // 登录
  @Post("/login")
  login(@Body() loginData: CreateAuthDto) {
    return this.authService.login(signupData)
  }
}

写了这么多,咱们先跑起来(pnpm run start:dev)看看nv_users这张表有没有创建,我这里推荐大家去装一个vscode插件
在这里插入图片描述
装了之后vscode左边会多一个这个东西,如果没有,重启vscode即可

在这里插入图片描述

如果有Navicat这个软件也是可以的,下面我们看下我们创建的表:

在这里插入图片描述
这里也是创建好了 Navicat中长这样,也是非常的像。

在这里插入图片描述

然后我们先简单写下auth.service.ts中的代码

import { Injectable } from '@nestjs/common';
import { CreateAuthDto } from './dto/create-auth.dto';
import { NV_Users } from './entities/auth.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';


@Injectable()
export class AuthService {
    constructor(@InjectRepository(NV_Users) private readonly user: Repository<NV_Users>) { }

    // 注册
    signup(signupData: CreateAuthDto) {
        console.log(signupData);
        return "注册成功"
    }

    // 登录
    login(loginData: CreateAuthDto) {
        console.log(loginData);
        return "登录成功"
    }
}

接着就是来测试下接口有没有问题,这里我使用apifox这个软件,当然其他接口测试软件也是可以的,比如postman等等。nestjs默认端口是3000,可以在main.ts去修改,我这里就使用默认的了,然后接口路径就是auth.controller.ts中拼接起来,比如我这里注册接口就是/auth/signup,其他同理。

在这里插入图片描述
在这里插入图片描述

接口测试没问题,接着就是写业务代码了,写业务代码前需要安装几个包:

pnpm i bcryptjs // 这个是对用户密码进行加密的
pnpm i @nestjs/jwt // 用于生成token

其中也是用到了几个方法:
bcryptjs.hashSync()方法是对用户密码进行加密用的,hashSync()方法第一个参数为用户密码,第二个为密码盐(简单理解为加密的程度)。
bcryptjs.compareSync()方法是对用户的密码和加密后的密码进行比较的,如果正确返回true,错误则返回fasle,第一个参数为登录时用户输入的密码,第二个为查出来的加密后的密码。这里提一下,加密密码的方法是不可逆的,也就是没有解密的功能,这样做也是保证用户的安全。
JwtService.sign()方法即生成token的方法,参数可传入我们想传入给前端的用户对象,这样前端可以把token拿去解密,拿到用户对象,切记不能将密码传入。

我们先新建一个关于token相关的配置文件constants.ts

export const jwtConstants = {
    secret: "leeKey", // 密钥
    expiresIn: "60s" // token有效时间  
}

接着在auth.module.ts中配置

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NV_Users } from './entities/auth.entity';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from "./constants"


@Module({
  imports: [TypeOrmModule.forFeature([NV_Users]), JwtModule.register({
    secret: jwtConstants.secret,
    signOptions: { expiresIn: jwtConstants.expiresIn }
  })],
  controllers: [AuthController],
  providers: [AuthService]
})
export class AuthModule { }

登录注册我这里就一次性写完了,代码看下面👇

import { BadRequestException, Injectable } from '@nestjs/common';
import { CreateAuthDto } from './dto/create-auth.dto';
import { NV_Users } from './entities/auth.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcryptjs from "bcryptjs"
import { JwtService } from "@nestjs/jwt"


@Injectable()
export class AuthService {
    constructor(
        @InjectRepository(NV_Users) private readonly user: Repository<NV_Users>,
        private readonly JwtService: JwtService
    ) { }

    // 注册
    async signup(signupData: CreateAuthDto) {

        const findUser = await this.user.findOne({
            where: { username: signupData.username }
        })
        if (findUser && findUser.username === signupData.username) return "用户已存在"
        // 对密码进行加密处理
        signupData.password = bcryptjs.hashSync(signupData.password, 10)
        await this.user.save(signupData)
        return "注册成功"
    }

    // 登录
    async login(loginData: CreateAuthDto) {

        const findUser = await this.user.findOne({
            where: { username: loginData.username }
        })
        // 没有找到
        if (!findUser) return new BadRequestException("用户不存在")

        // 找到了对比密码
        const compareRes: boolean = bcryptjs.compareSync(loginData.password, findUser.password)
        // 密码不正确
        if (!compareRes) return new BadRequestException("密码不正确")
        const payload = { username: findUser.username }

        return {
            access_token: this.JwtService.sign(payload),
            msg: "登录成功"
        }
    }
}

接着我们注册两个用户试一试

在这里插入图片描述
在这里插入图片描述

也是没有问题,接着测试登录,也是正确返回token

在这里插入图片描述
以上是关于登录注册功能,下面我们来看看身份验证这一块的功能,如何完成。
首先我们新建文件/common/public.decorator.ts,创建自定义装饰器

import { SetMetadata } from "@nestjs/common";

export const IS_PUBLIC_KEY = 'isPublic'
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

接着我们安装这几个包

pnpm install --save @nestjs/passport passport-jwt
pnpm install @types/passport-jwt --save-dev

新建jwt-auth.grard.ts文件,用于全局守卫,将未携带token的接口进行拦截,代码👇

import { ExecutionContext, Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport"
import { Reflector } from "@nestjs/core";
import { Observable } from "rxjs"
import { IS_PUBLIC_KEY } from "src/common/public.decorator";


@Injectable()

export class jwtAuth extends AuthGuard("jwt") {
    constructor(private reflector: Reflector) {
        super()
    }

    canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
        const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
            context.getHandler(),
            context.getClass()
        ])
        console.log(isPublic, "isPublic");
        if (isPublic) return true
        return super.canActivate(context)
    }
}

新建jwt-auth.strategy.ts,该文件为验证策略,也就是验证前端请求头中携带的token,代码👇

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { jwtConstants } from "./constants";


export interface JwtPayload {
    username: string
}


@Injectable()
// 验证请求头中的token
export default class JwtAuthStrategy extends PassportStrategy(Strategy, "jwt") {
    constructor() {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: jwtConstants.secret
        })
    }

    async validate(payload: JwtPayload) {
        console.log(payload.username);
        const { username } = payload
        return {
            username
        }
    }
}

接着在auth.module.tsproviders中配置JwtAuthStrategy,代码👇

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NV_Users } from './entities/auth.entity';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from "./constants"
import { JwtAuthStrategy } from "./jwt-auth.strategy"


@Module({
  imports: [TypeOrmModule.forFeature([NV_Users]), JwtModule.register({
    secret: jwtConstants.secret,
    signOptions: { expiresIn: jwtConstants.expiresIn }
  })],
  controllers: [AuthController],
  providers: [AuthService, JwtAuthStrategy]
})
export class AuthModule { }

最后在app.module.ts将其注册为全局守卫

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard } from './auth/jwt-auth.grard';

@Module({
  imports: [AuthModule, TypeOrmModule.forRoot({
    type: 'mysql', // 数据库类型
    host: 'localhost', // 主机名
    port: 3306, // 端口
    username: 'root', // 用户名
    password: '123456', // 密码
    database: 'nestjs', // 数据库名称
    synchronize: true,
    retryDelay: 500, //重试连接数据库间隔
    retryAttempts: 10,//重试连接数据库的次数
    autoLoadEntities: true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
  }),],
  controllers: [AppController],
  // 注册为全局守卫
  providers: [AppService, {
    provide: APP_GUARD,
    useClass: JwtAuthGuard
  }],
})
export class AppModule { }

以上配置完成后,再次请求注册接口,返回401,表示我们没有权限

在这里插入图片描述
然后我们给通用接口(注册和登录接口)都加上@Public装饰器后

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateAuthDto } from './dto/create-auth.dto';
import { Public } from 'src/common/public.decorator';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) { }


  // 注册
  @Public()
  @Post("/signup")
  // @Public()
  signup(@Body() signupData: CreateAuthDto) {
    return this.authService.signup(signupData)
  }

  // 登录
  @Public()
  @Post("/login")
  login(@Body() loginData: CreateAuthDto) {
    return this.authService.login(loginData)
  }
}

再去请求,发现没有问题

在这里插入图片描述

源码地址

以上就是关于全局身份验证的内容了,这样做之后,我们只需要给你通用接口加上@Pulic()装饰器即可,随后我们需要验证的接口就不用一个一个去加守卫了。
以上就是本次文章的全部内容,创作不易,如有帮助,记得点赞收藏,感谢您的观看。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
NestJS 是一个非常强大的后端框架,它提供了许多工具和库来帮助我们实现用户注册登录验证功能。下面是一个基本的 NestJS 用户注册登录验证功能的实现步骤: 1. 安装必要的库 首先,我们需要安装一些必要的库: ```bash npm install @nestjs/passport passport passport-local bcryptjs jsonwebtoken ``` 这些库将帮助我们实现身份验证和密码加密。 2. 创建用户模型 我们需要创建一个用户模型,用于存储用户的用户名和密码。在 NestJS 中,我们可以使用 Mongoose 来定义和操作 MongoDB 数据库。 ```bash nest g class users/user ``` ```typescript import * as mongoose from 'mongoose'; export const UserSchema = new mongoose.Schema({ username: String, password: String, }); export interface User extends mongoose.Document { id: string; username: string; password: string; } ``` 3. 创建用户服务 我们需要创建一个用户服务,用于处理用户注册登录验证逻辑。 ```bash nest g service users/user ``` ```typescript import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { User } from './user.interface'; import * as bcrypt from 'bcryptjs'; @Injectable() export class UserService { constructor(@InjectModel('User') private readonly userModel: Model<User>) {} async findByUsername(username: string): Promise<User | undefined> { return await this.userModel.findOne({ username }).exec(); } async create(user: User): Promise<User> { const salt = bcrypt.genSaltSync(); user.password = bcrypt.hashSync(user.password, salt); const newUser = new this.userModel(user); return await newUser.save(); } async comparePassword(password: string, hash: string): Promise<boolean> { return bcrypt.compareSync(password, hash); } } ``` 4. 创建身份验证策略 我们需要创建一个身份验证策略,用于验证用户的用户名和密码。在 NestJS 中,我们可以使用 Passport.js 来实现身份验证。 ```bash nest g guard auth/auth ``` ```typescript import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class AuthGuardLocal extends AuthGuard('local') {} ``` 5. 创建身份验证模块 我们需要创建一个身份验证模块,用于注册身份验证策略和提供身份验证服务。在 NestJS 中,我们可以使用 Passport.js 和 JWT 来实现身份验证。 ```bash nest g module auth ``` ```typescript import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { PassportModule } from '@nestjs/passport'; import { JwtModule } from '@nestjs/jwt'; import { AuthService } from './auth.service'; import { AuthController } from './auth.controller'; import { UserService } from '../users/user.service'; import { UserSchema } from '../users/user.model'; import { JwtStrategy } from './jwt.strategy'; import { LocalStrategy } from './local.strategy'; @Module({ imports: [ MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]), PassportModule, JwtModule.register({ secret: 'secret', signOptions: { expiresIn: '1d' }, }), ], providers: [AuthService, UserService, LocalStrategy, JwtStrategy], controllers: [AuthController], }) export class AuthModule {} ``` 6. 创建身份验证控制器 我们需要创建一个身份验证控制器,用于处理用户注册登录验证请求。 ```bash nest g controller auth ``` ```typescript import { Controller, Post, UseGuards, Request, Body, Get, } from '@nestjs/common'; import { AuthGuardLocal } from './auth.guard'; import { AuthService } from './auth.service'; import { UserService } from '../users/user.service'; import { User } from '../users/user.interface'; import { JwtAuthGuard } from './jwt.guard'; @Controller() export class AuthController { constructor( private authService: AuthService, private userService: UserService, ) {} @UseGuards(AuthGuardLocal) @Post('auth/login') async login(@Request() req) { return this.authService.login(req.user); } @Post('auth/register') async register(@Body() user: User) { return this.userService.create(user); } @UseGuards(JwtAuthGuard) @Get('profile') async getProfile(@Request() req) { return req.user; } } ``` 7. 创建身份验证策略 我们需要创建一个身份验证策略,用于验证用户的 JWT token。在 NestJS 中,我们可以使用 Passport.js 和 JWT 来实现身份验证。 ```bash nest g guard auth/jwt ``` ```typescript import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') {} ``` 8. 创建身份验证策略 我们需要创建一个身份验证策略,用于验证用户的用户名和密码。在 NestJS 中,我们可以使用 Passport.js 来实现身份验证。 ```bash nest g guard auth/local ``` ```typescript import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { AuthService } from './auth.service'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private authService: AuthService) { super(); } async validate(username: string, password: string): Promise<any> { const user = await this.authService.validateUser(username, password); if (!user) { return false; } return user; } } ``` 9. 创建 JWT 策略 我们需要创建一个 JWT 策略,用于验证用户的 JWT token。在 NestJS 中,我们可以使用 Passport.js 和 JWT 来实现身份验证。 ```typescript import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { UserService } from '../users/user.service'; import { User } from '../users/user.interface'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private readonly userService: UserService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'secret', }); } async validate(payload: any): Promise<User> { return await this.userService.findByUsername(payload.username); } } ``` 现在我们已经完成NestJS 用户注册登录验证功能的实现。我们可以通过发送 POST 请求到 `/auth/register` 端点来注册新用户,通过发送 POST 请求到 `/auth/login` 端点来登录,通过发送 GET 请求到 `/profile` 端点来获取用户信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值