NestJS-身份验证JWT的使用以及登录注册

登录注册以及身份验证

NestJS-登录注册

这里我们后续依赖使用到的几个依赖模块如下(可以提前下载好)

javascript 复制代码
npm install bcrypt // 加密密码
npm install class-validator  //装饰器来验证数据的有效性

注册模块

register.dto.ts注册规则

新建一个register.dto.ts存放我们注册部分规则

javascript 复制代码
// src/modules/user/dto/register.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';

export class RegisterDto {
  @IsString()
  username: string;
  @IsString()
  @MinLength(3)
  password: string;
}

user.service.ts

javascript 复制代码
import { Injectable ,ConflictException} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { RegisterDto } from './dto/register.dto';
@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}
  // 获取所有用户
  async findAll(): Promise<User[]> {
    return this.userRepository.find();
  }
  async register(registerDto: RegisterDto): Promise<User> {
    // 检查用户是否已经存在
    const existingUser = await this.userRepository.findOne({ where: { username: registerDto.username } });
    if (existingUser) {
      throw new ConflictException('Username already exists');
    }
    // 创建新用户
    const user = new User();
    user.username = registerDto.username;
    user.password = registerDto.password;

    // 保存到数据库
    return this.userRepository.save(user);
  }
}

user.controller.ts

更改一下我们使用的注册接口

javascript 复制代码
import { Controller, Get, Post, Param, Body, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { RegisterDto } from './dto/register.dto';
@Controller()
export class UserController {
  constructor(private readonly userService: UserService) {}
  @Get('users')
  async findAll(): Promise<User[]> {
    return this.userService.findAll();
  }
  @Post('register')
  async register(@Body() registerDto: RegisterDto): Promise<User> {
    return this.userService.register(registerDto);
  }
}

检测注册模块

接下来我们测试一下我们的数据模块,用户存在的时候,这里给我们的提示

javascript 复制代码
{
    "message": "Username already exists",
    "error": "Conflict",
    "statusCode": 409
}
👉手动设置请求的返回格式

后续优化看代码,这里只是简单返回一下

javascript 复制代码
 return {
  data: [],
  message: '用户已存在!',
  code: 400,
};
👉再次注册
javascript 复制代码
//存在用户
{
    "code": 400,
    "message": "用户已存在!"
}



//新用户注册
{
    "message": "恭喜你,您的账号注册成功!",
    "code": 200
}

登录模块编写

这里我们直接也是将登录编写进了用户部分,先以实现功能为主

👉用户dto部分

先编写一个用户对应的规则dto部分

javascript 复制代码
// src/modules/user/dto/login.dto.ts
import { IsString, IsEmail, MinLength } from 'class-validator';
export class LoginDto{
  @IsString()
  username: string;
  @IsString()
  @MinLength(3)
  password: string;
}
👉user.service.ts之中添加登录方法
javascript 复制代码
import { LoginDto } from './dto/login.dto';

// 异步登录
  async login(loginDto: LoginDto){
  // 查询数据库中是否存在同名用户

  // 1. 查询数据库中是否存在该用户
  const loginUser = await this.userRepository.findOne({
    where: { username: loginDto.username },
  });

   // 2. 如果用户不存在,返回异常信息
   if (!loginUser) {
    throw new HttpException(
      {
        code: 404, // 适当的错误码是 404 (用户未找到)
        message: '用户不存在!',
      },
      HttpStatus.NOT_FOUND, // 使用 404 状态码
    );
  }
  // 3. 比较密码是否正确
  if (loginDto.password  !==  loginUser.password) {
    throw new HttpException(
      {
        code: 401, // 未授权错误码
        message: '密码错误!',
      },
      HttpStatus.UNAUTHORIZED, // 使用 401 状态码
    );
  }

  // 4. 返回登录成功信息,可以添加 JWT 或 Session 等信息
  return {
    message: '登录成功!',
    code: 200,
    data: {
      username: loginUser.username,
      userId: loginUser.userId,
      // 可以附带其他的用户信息,如权限、token等
    },
  };
}
👉user.controller.ts之中使用

上面的方法服务写完了,但是我们还没有在控制器之中使用,这个时候需要使用一下

可以先导入我们的dto规则部分

javascript 复制代码
import { LoginDto } from './dto/login.dto';

post接口调佣

javascript 复制代码
 @Post('login')
// 登录方法
 login(@Body() loginDto: LoginDto){
  // 调用userService中的login方法
  return this.userService.login(loginDto);
}

核查登录模块

👉核查登录

接下来我们尝试调一下我们的登录接口

javascript 复制代码
http://localhost:8888/api/login
👉登录正确时候
javascript 复制代码
{
    "message": "登录成功!",
    "code": 200,
    "data": {
        "username": "admin",
        "userId": 6
    }
}
👉登录错误时候
javascript 复制代码
{
    "code": 404,
    "message": "用户不存在!"
}

这个时候我们的登录和注册模块功能已经好了

身份验证

JWT认识安装

JWT作用

  1. 用户身份验证:先解析请求中的 token,验证它是否合法。通过中间件或守卫 (guard) 来实现。
  2. 使用 JWT 进行认证 :使用 JWT(JSON Web Token)进行用户认证,使用 @nestjs/jwt 包来解析和验证 token。
  3. 从 token 获取用户信息:验证 token通过,从中提取出用户信息,通过服务返回该用户详细数据

jwt依赖安装

javascript 复制代码
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install @types/passport-jwt --save-dev
相关包的解释
javascript 复制代码
@nestjs/jwt:用于生成和验证 JWT
passport-jwt:Passport JWT 策略模块
passport:用于 NestJS 认证的包
@nestjs/passport:NestJS 封装的 Passport 模块

生成auth授权模块

👉 生成一个auth授权模块,生成对应的auth授权模块

javascript 复制代码
nest g controller auth --no-spec
nest g module auth --no-spec
nest g service auth --no-spec

👉生成文件如下

javascript 复制代码
  auth.controller.ts
  auth.module.ts
  auth.service.ts

生成token信息

👉文件auth.module.ts搭建

javascript 复制代码
// src/auth/auth.module.ts
import { Module,forwardRef } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from '@/modules/user/user.module';
@Module({
  imports: [
    forwardRef(() => UserModule),  // 使用 forwardRef() 解决循环依赖
    JwtModule.register({
      secret: 'lintaibai^_^',  // 可以放到环境变量中
      signOptions: { expiresIn: '24h' },  // 设置 token 过期时间
    }),
    // UserModule, //导入用户模块
  ],
  providers: [AuthService],
  controllers: [AuthController],
})
export class AuthModule {}

👉文件auth.controller.ts搭建

文件位于src=> auth =>auth.controller.ts文件,接下来我们在这个文件里面生成token信息

javascript 复制代码
// src/auth/auth.controller.ts
import { Controller,Post,Get,Body,Headers} from '@nestjs/common';
import { AuthService } from '@/auth/auth.service';

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

  @Post('login')
  async login(@Body() loginDto: { username: string; password: string }) {
    console.log('login-loginDto', loginDto);
    return this.authService.login(loginDto);
  }
}

👉文件auth.service.ts

接下来我们将登录接口迁移进来,在登录的时候给用户返回Token信息

javascript 复制代码
import { Injectable, HttpException, HttpStatus,Headers } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Code, Repository } from 'typeorm';
import { UserService } from '@/modules/user/user.service'
@Injectable()
export class AuthService {
  constructor(
    private jwtService: JwtService,
    private readonly userService: UserService,
  ) {}
  
  // 生成 JWT token
  async generateToken(user: any): Promise<string>{
    return this.jwtService.sign(
      {userId: user.userId,username: user.username},
    )
  }
  // 验证 JWT token
  async validateToken(token: string){
    try {
      const decoded = this.jwtService.verify(token);
      console.log(decoded,'decoded');
      // 根据 decoded 提取用户信息,返回用户对象
      return {
        user:decoded,
        code: 200,
        message:'欢迎你的登录!',
     }; // 示例
    } catch (error) {
      throw new HttpException(
        {
          code: 401, // 适当的错误码是 404 (用户未找到)
          message: 'token已经过期!',
        },
        HttpStatus.UNAUTHORIZED, // 
      );
    }
  }

  // 验证用户是否存在
  async login(LoginDto){
    const loginUser = await this.userService.findByUsername(LoginDto.username);
     // const user = await this.authService.validateUserCredentials(body.username, body.password);
    console.log(loginUser,'loginUser--------------------------------------------');
    if (!loginUser) {
      throw new HttpException(
        {
          code: 404, // 适当的错误码是 404 (用户未找到)
          message: '用户不存在!',
        },
        HttpStatus.NOT_FOUND, // 使用 404 状态码
      );
    }else{
      // 验证用户凭证
      const token = await this.generateToken(loginUser);
      console.log(token,'用户登录token信息');
      return {
        message: '登录成功!',
        code: 200,
        token:token,
      };
    }
  }
 }

👉登录校验

点击登录,这个时候给我们返回的信息我们就已拿到了

javascript 复制代码
{
    "message": "登录成功!",
    "code": 200,
    "token": "zMH0.kj_j7Ujg1iNoiPJcPMLkOy12UUNOpL6QMjxE"
}

getInfo获取用户信息

👉auth.controller.ts添加接口

javascript 复制代码
// 获取用户信息token
  @Get('getInfo')
  async getInfo(@Headers('authorization') authorization: string) {
    return this.authService.getInfo(authorization);
  }

👉auth.service.ts添加方法

javascript 复制代码
//导入相关方法
import { Injectable, HttpException, HttpStatus,Headers } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Code, Repository } from 'typeorm';
import { UserService } from '@/modules/user/user.service'


// 获取用户信息
  async getInfo(authHeader: string){
    console.log(authHeader,'authHeader');
    if (!authHeader) {
      throw new HttpException(
        {
          code: 401, // 未授权错误码
          message: '未授权Token',
        },
        HttpStatus.UNAUTHORIZED, // 使用 401 状态码
      );
    }
    const token = authHeader.split(' ')[1]; // 提取 Bearer token
    if (!token) {
      throw new HttpException(
        {
          code: 401, // 未授权错误码
          message: 'Token无效',
        },
        HttpStatus.UNAUTHORIZED, // 使用 401 状态码
      );
    }else {
      // 验证token
      const decoded = this.jwtService.verify(token);
      if(!decoded){
        throw new HttpException(
          {
            code: 401, // 未授权错误码
            message: 'Token无效',
          },
          HttpStatus.UNAUTHORIZED, // 使用 401 状态码
        );
      }else {
        return {
          code: 200,
          message: '验证成功,欢迎你的登录!',
          data: decoded,
        };
      }
    }
  }

👉模块auth.module.ts添加auth.module.ts模块

这里我们需要注意的是,必须在auth.module.ts引入用户部分的模块部分

注意这里的forwardRef 方法,必须使用这个来解决循环依赖

javascript 复制代码
// src/auth/auth.module.ts
import { Module,forwardRef } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UserModule } from '@/modules/user/user.module';
import { JwtStrategy } from './jwt.strategy';  // 后续会创建的 JWT 策略
// import { UserService } from '@/modules/user/user.service'
@Module({
  imports: [
    forwardRef(() => UserModule),  // 使用 forwardRef() 解决循环依赖
    JwtModule.register({
      secret: 'lintaibai^_^',  // 可以放到环境变量中
      signOptions: { expiresIn: '24h' },  // 设置 token 过期时间
    }),
    // UserModule, //导入用户模块
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
})
export class AuthModule {}

👉请求getInfo用户信息接口

这个时候我们已经可以从接口之中拿到对应的用户信息了

javascript 复制代码
{
    "code": 200,
    "message": "验证成功,欢迎你的登录!",
    "data": {
        "userId": 6,
        "username": "admin",
        "iat": 1745999430,
        "exp": 1746085830
    }
}
相关推荐
小高0079 小时前
🔍ECMAScript 2025 有哪些新特性?
前端·javascript
Hashan9 小时前
elpis-core:基于 Koa 的轻量级 Web 应用框架
前端·javascript·node.js
前端Hardy9 小时前
轻松搞定JavaScript数组方法,面试被问直接答!
前端·javascript·面试
云枫晖9 小时前
手写Promise-catch和finally
前端·javascript
薄雾晚晴9 小时前
大屏开发实战:封装自动判断、无缝衔接的文字滚动组件,告别文本截断烦恼
前端·javascript·vue.js
Eoch779 小时前
HashMap夺命十连问,你能撑到第几轮?
java·后端
每天进步一点_JL9 小时前
🔥 一个 synchronized 背后,JVM 到底做了什么?
后端
SamDeepThinking9 小时前
有了 AI IDE 之后,为什么还还要 CLI?
后端·ai编程·cursor
yinke小琪9 小时前
线程池七宗罪:你以为的优化其实是在埋雷
java·后端·面试
Beginner x_u9 小时前
前端八股文 Vue上
前端·javascript·vue.js·八股文