Skip to content

243. 综合实战:开放世界(四十四)

Published:

上节跑通了注册流程,这节来做登录。

2026-04-05 22.30.48.gif

image.png

进入后端项目

首先加一下 user/dto/login-user.dto.ts

import { IsString, MaxLength, MinLength } from 'class-validator';

export class LoginUserDto {
  @IsString()
  @MinLength(3)
  @MaxLength(50)
  username: string;

  @IsString()
  @MinLength(1)
  @MaxLength(100)
  password: string;
}

dto 是接收参数的,传入这俩字段。

在 UserController 加一个接口:

import { Body, Controller, Post } from '@nestjs/common';
import { UserService } from './user.service';
import { LoginUserDto } from './dto/login-user.dto';
import { RegisterUserDto } from './dto/register-user.dto';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post('register')
  register(@Body() registerUserDto: RegisterUserDto) {
    return this.userService.register(registerUserDto);
  }

  @Post('login')
  login(@Body() loginUserDto: LoginUserDto) {
    return this.userService.login(loginUserDto);
  }
}

然后安装 jwt 的包,用来做 jwt 的生成:

pnpm install @nestjs/jwt

在 UserService 里加一下:

image.png

import {
  ConflictException,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { LoginUserDto } from './dto/login-user.dto';
import { RegisterUserDto } from './dto/register-user.dto';
import { User } from './entities/user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly jwtService: JwtService,
  ) {}

  async register(dto: RegisterUserDto) {
    const existing = await this.userRepository.findOne({
      where: { username: dto.username },
    });
    if (existing) {
      throw new ConflictException('用户名已被占用');
    }
    const passwordHash = await bcrypt.hash(dto.password, 10);
    const user = this.userRepository.create({
      username: dto.username,
      password: passwordHash,
    });
    await this.userRepository.save(user);
    return {
      id: user.id,
      username: user.username,
      createdAt: user.createdAt,
    };
  }

  async login(dto: LoginUserDto) {
    const user = await this.userRepository.findOne({
      where: { username: dto.username },
    });
    if (!user) {
      throw new UnauthorizedException('用户名或密码错误');
    }
    const ok = await bcrypt.compare(dto.password, user.password);
    if (!ok) {
      throw new UnauthorizedException('用户名或密码错误');
    }
    const payload = { sub: user.id, username: user.username };
    return {
      accessToken: await this.jwtService.signAsync(payload),
      user: {
        id: user.id,
        username: user.username,
      },
    };
  }
}

还要在 UserModule 引入 JwtModule

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User } from './entities/user.entity';

@Module({
  imports: [
    TypeOrmModule.forFeature([User]),
    JwtModule.register({
      secret: process.env.JWT_SECRET ?? 'open-world-dev-secret',
      signOptions: { expiresIn: '7d' },
    }),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

跑起来测试下:

npm run start:dev

用这个 curl

curl -s -X POST http://localhost:3000/user/login \
 -H "Content-Type: application/json" \
 -d '{"username":"guang","password":"11111111"}'

这里替换成你在界面注册的用户信息

image.png

image.png

没啥问题,返回了 jwt 就代表登录成功了。

总结

这节我们实现了登录接口。

username 和 password 校验成功后,返回 jwt

之后前端携带 jwt 就可以访问需要登录的信息了。

评论