0
0
Lập trình
TT

Hướng Dẫn Từng Bước Thêm Hệ Thống Người Dùng vào Nest.js

Đăng vào 2 tuần trước

• 8 phút đọc

Hướng Dẫn Từng Bước Thêm Hệ Thống Người Dùng vào Nest.js

Trong bài hướng dẫn trước, chúng ta đã xây dựng một blog cơ bản hỗ trợ tạo bài viết. Tuy nhiên, một vấn đề lớn là blog này không có cơ chế xác thực, nghĩa là bất kỳ ai cũng có thể tạo hoặc xóa bài viết. Để khắc phục điều này, trong bài viết này, chúng ta sẽ thêm một hệ thống người dùng và xác thực cho blog.

Giới Thiệu Về Các Phương Thức Xác Thực

Trong phát triển web, có hai phương thức xác thực phổ biến nhất là Dựa trên Token (ví dụ: JWT)Dựa trên Session (Cookie).

  • JWT (JSON Web Tokens): Đây là phương thức xác thực phổ biến nhất hiện nay. Sau khi người dùng đăng nhập, máy chủ tạo một token và trả về cho client. Client sẽ gửi token này trong các yêu cầu tiếp theo và máy chủ chỉ cần xác minh token chưa bị thay đổi. Phương thức này rất thích hợp cho các trang web lớn với nhiều máy chủ, vì nó tránh việc phải đồng bộ hóa trạng thái người dùng liên tục giữa các máy chủ.
  • Session-Cookie: Sau khi người dùng đăng nhập, máy chủ tạo một session và trả về Session ID cho trình duyệt qua cookie. Trình duyệt tự động gửi cookie này trong các yêu cầu tiếp theo. Máy chủ sẽ sử dụng Session ID trong cookie để tìm thông tin session tương ứng đã lưu trữ, từ đó xác định người dùng.

Trong bài hướng dẫn này, chúng ta sẽ chọn phương thức Session-Cookie. Blog của chúng ta là một ứng dụng đơn giản với kiến trúc truyền thống, vì vậy việc sử dụng Session-Cookie cho xác thực là cách tiếp cận trực tiếp, cổ điển và an toàn nhất. Nest.js cũng hỗ trợ rất tốt cho phương thức này.

Tạo Module Người Dùng

Trước khi xử lý xác thực, chúng ta hãy thêm một hệ thống người dùng trước.

Tương tự như việc tạo module posts trong bài hướng dẫn trước, hãy sử dụng Nest CLI để nhanh chóng tạo các tệp cần thiết:

Copy
nest generate module users
nest generate controller users
nest generate service users

Tiếp theo, tạo một tệp thực thể người dùng user.entity.ts trong thư mục src/users để ánh xạ đến bảng users trong cơ sở dữ liệu.

typescript Copy
// src/users/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ unique: true })
  username: string;

  @Column()
  password: string; // Mật khẩu sẽ được mã hóa
}

Trong UsersModule (src/users/users.module.ts), đăng ký TypeOrmModule để nó có thể hoạt động trên thực thể User. Lưu ý rằng bạn cần xuất UsersService để có thể sử dụng trong các module khác sau này (như module Auth).

typescript Copy
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService], // Xuất UsersService để sử dụng trong các module khác
})
export class UsersModule {}

Cuối cùng, thực hiện câu lệnh SQL sau trong cơ sở dữ liệu PostgreSQL mà chúng ta đã tạo trong bài hướng dẫn trước để tạo bảng user:

Copy
CREATE TABLE "user" (
    "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    "username" VARCHAR UNIQUE NOT NULL,
    "password" VARCHAR NOT NULL
);

Nếu cơ sở dữ liệu của bạn được tạo trên Leapcell,

bạn có thể dễ dàng thực thi các câu lệnh SQL bằng giao diện đồ họa. Chỉ cần vào trang quản lý cơ sở dữ liệu trên website, dán câu lệnh ở trên vào giao diện SQL và chạy nó.

Thực Hiện Đăng Ký Người Dùng

Chỉnh sửa src/users/users.service.ts để thêm logic cho việc tạo và tìm người dùng.

Lưu ý: Để đảm bảo an toàn, mật khẩu của người dùng cần được mã hóa trước khi lưu trữ trong cơ sở dữ liệu. Chúng ta sẽ sử dụng thư viện bcrypt để mã hóa mật khẩu.

Cài đặt các phụ thuộc cần thiết:

Copy
npm install bcrypt
npm install -D @types/bcrypt
typescript Copy
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>
  ) {}

  async create(user: Partial<User>): Promise<User> {
    const saltRounds = 10;
    const hashedPassword = await bcrypt.hash(user.password || '', saltRounds);

    const newUser = this.usersRepository.create({
      username: user.username,
      password: hashedPassword,
    });

    return this.usersRepository.save(newUser);
  }

  async findOne(username: string): Promise<User | null> {
    return this.usersRepository.findOneBy({ username });
  }
}

Tạo Module Xác Thực và Logic Xác Nhận Đăng Nhập

Chúng ta sẽ đặt tất cả logic liên quan đến xác thực trong một module auth riêng biệt. Đầu tiên, chúng ta sẽ chỉ thực hiện logic cốt lõi "xác thực người dùng".

Sử dụng CLI để tạo module authservice:

Copy
nest generate module auth
nest generate service auth

Chỉnh sửa src/auth/auth.service.ts để thêm phương thức validateUser xác minh xem tên người dùng và mật khẩu có đúng hay không.

typescript Copy
// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && (await bcrypt.compare(pass, user.password))) {
      const { password, ...result } = user;
      return result; // Xác thực thành công, trả về thông tin người dùng không có mật khẩu
    }
    return null; // Xác thực thất bại
  }
}

Bây giờ, nhập UsersModule vào AuthModule để AuthService có thể sử dụng UsersService.

typescript Copy
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [UsersModule],
  controllers: [AuthController],
  providers: [AuthService],
})
export class AuthModule {}

Tạo Trang Đăng Nhập và Đăng Ký

Chúng ta cần cung cấp một giao diện để người dùng tương tác. Trong thư mục views, tạo login.ejsregister.ejs.

register.ejs

html Copy
<%- include('_header', { title: 'Đăng Ký' }) %>

<form action="/users/register" method="POST" class="post-form">
  <h2>Đăng Ký</h2>
  <div class="form-group">
    <label for="username">Tên người dùng</label>
    <input type="text" id="username" name="username" required />
  </div>
  <div class="form-group">
    <label for="password">Mật khẩu</label>
    <input type="password" id="password" name="password" required />
  </div>
  <button type="submit">Đăng Ký</button>
</form>

<%- include('_footer') %>

login.ejs

html Copy
<%- include('_header', { title: 'Đăng Nhập' }) %>

<form action="/auth/login" method="POST" class="post-form">
  <h2>Đăng Nhập</h2>
  <div class="form-group">
    <label for="username">Tên người dùng</label>
    <input type="text" id="username" name="username" required />
  </div>
  <div class="form-group">
    <label for="password">Mật khẩu</label>
    <input type="password" id="password" name="password" required />
  </div>
  <button type="submit">Đăng Nhập</button>
</form>

<%- include('_footer') %>

Thực Hiện Routing và Logic Controller

Bây giờ, chúng ta sẽ tạo các route để xử lý yêu cầu đăng ký và đăng nhập.

Route Đăng Ký

Cập nhật src/users/users.controller.ts để xử lý hiển thị trang đăng ký và gửi biểu mẫu:

typescript Copy
// src/users/users.controller.ts
import { Controller, Get, Post, Render, Body, Res } from '@nestjs/common';
import { UsersService } from './users.service';
import { Response } from 'express';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get('register')
  @Render('register')
  showRegisterForm() {
    return;
  }

  @Post('register')
  async register(@Body() body: any, @Res() res: Response) {
    // Để đơn giản, không có xác thực phức tạp ở đây
    await this.usersService.create(body);
    res.redirect('/auth/login'); // Chuyển hướng đến trang đăng nhập sau khi đăng ký thành công
  }
}

Route Xác Thực Đăng Nhập

Tạo một auth.controller.ts mới để xử lý các yêu cầu đăng nhập.

Copy
nest generate controller auth

Chỉnh sửa src/auth/auth.controller.ts. Tại đây, chúng ta sẽ gọi phương thức validateUser. Nếu xác thực thành công, chúng ta sẽ chuyển hướng đến trang chính của blog.

Nếu thất bại, để đơn giản, chúng ta sẽ không xử lý đặc biệt cho thời điểm này. Trong một ứng dụng thực tế, bạn thường sẽ trả về một thông báo lỗi.

typescript Copy
// src/auth/auth.controller.ts
import { Controller, Get, Post, Render, Body, Res, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
import { Response } from 'express';

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

  @Get('login')
  @Render('login')
  showLoginForm() {
    return;
  }

  @Post('login')
  async login(@Body() body: any, @Res() res: Response) {
    const user = await this.authService.validateUser(body.username, body.password);
    if (!user) {
      throw new UnauthorizedException();
    }
    // Xác thực thành công
    res.redirect('/posts');
  }
}

Cuối cùng, nhập UsersModuleAuthModule vào app.module.ts.

typescript Copy
// src/app.module.ts
// ... imports
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    // ... TypeOrmModule.forRoot(...)
    PostsModule,
    UsersModule,
    AuthModule,
  ],
  // ...
})
export class AppModule {}

Tại thời điểm này, chúng ta đã hoàn thành một hệ thống đăng ký người dùng cơ bản và logic xác thực đăng nhập. Người dùng có thể tạo tài khoản, và ứng dụng có thể xác minh danh tính của họ.

Hãy thử nghiệm! Khởi động dự án của bạn:

Copy
npm run start:dev

Truy cập http://localhost:3000/users/register để đăng ký.

Sau khi đăng ký thành công, bạn sẽ tự động được chuyển hướng đến http://localhost:3000/auth/login để đăng nhập.

Bạn có thể thử nghiệm kết quả khi nhập đúng và sai thông tin tài khoản. Ví dụ, nếu bạn nhập sai thông tin, trang sẽ hiển thị lỗi 401 Unauthorized.

Tuy nhiên, việc đăng nhập hiện tại chỉ là một quá trình một lần, hoàn toàn để trải nghiệm quy trình xác thực đăng nhập; máy chủ không ghi nhớ trạng thái đăng nhập của người dùng.

Trong bài viết tiếp theo, chúng ta sẽ hoàn thiện logic xác thực đăng nhập để đạt được sự bền vững thật sự của phiên người dùng và hạn chế quyền truy cập vào các trang và tính năng dựa trên quyền hạn của người dùng.


Theo dõi chúng tôi trên X: @LeapcellHQ


Đọc thêm trên blog của chúng tôi

Các Bài Viết Liên Quan:

  • Cách Hosting Dự Án Golang Miễn Phí (Ví Dụ với Gin)
  • Cách Hosting Dự Án Rust Trên Đám Mây Miễn Phí
  • Cách Chạy Puppeteer Trên Đám Mây Miễn Phí: So Sánh Các Giải Pháp
  • Vercel - Giải Pháp Tốt Nhất Để Hosting Dự Án Next.js
Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào