JWT аутентификация в NestJS
Аутентификация является одним из наиболее важных аспектов любого приложения. Это повышает безопасность приложения, проверяя пользователей перед предоставлением им доступа к различным частям приложения. Аутентификация также позволяет компаниям отслеживать, сколько людей используют их продукты. В этом руководстве будет продемонстрирован пошаговый процесс реализации аутентификации пользователя JWT в NestJS.
Предпосылки
Этот урок представляет собой практическую демонстрацию. Чтобы продолжить, убедитесь, что у вас установлено следующее:
Настройка проекта
Чтобы настроить проект, вам сначала необходимо установить Nest CLI глобально с помощью следующей команды:
npm i - g @nestjs / cli
После завершения установки создайте новый проект, например:
nest new auth-with-nest
Далее вам будет предложено выбрать менеджер пакетов для установки зависимостей. Для этой демонстрации мы будем использовать Yarn.
Выберите yarn
и нажмите клавишу Enter . Теперь подождите, пока Yarn установит все необходимые зависимости, необходимые для запуска приложения.
Настройка базы данных MongoDB
Чтобы настроить и подключить базу данных, установите пакет Mongoose, bcrypt и оболочку NestJS с помощью следующей команды:
npm install -- сохранить @nestjs / mongoose @types / bcrypt mongoose bcrypt
Теперь обновите файл app.module.ts
и настройте Mongoose следующим образом:
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [MongooseModule.forRoot('mongodb://localhost/authentication')],
})
В приведенном выше фрагменте мы импортировали MongooseModule
в корень AppModule
.
Создание пользовательского модуля
Чтобы ваш код был чистым и хорошо организованным, создайте модуль специально для пользователей NestJS CLI, выполнив следующую команду:
nest g module users
Приведенный выше код создает пользовательскую папку users
с файлом module.ts
и файлом обновлений pp.module.ts
.
Создание пользовательской схемы
Чтобы создать пользовательскую схему, создайте файл users.model.ts
в папке src/users
и добавьте следующий код:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type UserDocument = User & Document;
@Schema()
export class User {
@Prop()
username: string;
@Prop()
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
Здесь мы определили форму нашей User
схемы с помощью @Schema()
декоратора и @Prop()
декоратора.
Mongoose сопоставит схему с коллекцией MongoDB. Схема определяет форму документов коллекции.
Теперь замените код в файле user/user.module.ts
и сделайте доступным в импорте userSchema
следующий код:
import { Module } from '@nestjs/common';
import { UsersService } from './user.service';
import { UsersController } from './user.controller';
import { MongooseModule } from "@nestjs/mongoose"
import { UserSchema } from "./user.model"
@Module({
imports: [MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
providers: [UsersService],
controllers: [UsersController]
})
export class UserModule {}
Создание пользовательского сервиса
Создав пользовательскую схему, выполните приведенную ниже команду, чтобы создать пользовательскую службу:
nest g module users
Этот код создает файл users.service.ts
и обновляет файл app.module.ts
.
NB, вы можете создавать свои файлы и папки вручную, но интерфейс командной строки NestJS облегчит вам жизнь, автоматически обновляя необходимые папки.
Теперь добавьте в файл users.service.ts
следующий код:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from './users.model';
@Injectable()
export class UsersService {
constructor(@InjectModel('user') private readonly userModel: Model<UserDocument>) { }
async createUser(username: string, password: string): Promise<User> {
return this.userModel.create({
username,
password,
});
}
async getUser(query: object ): Promise<User> {
return this.userModel.findOne(query);
}
}
Здесь мы использовали @InjectModel()
декоратор для внедрения userModel
в файл UsersService
.
Создание пользовательского контроллера
Теперь давайте создадим пользовательский контроллер для определения маршрутов API:
nest g service users
Добавьте код в файл users.controller.ts
:
import { Body, Controller, Post, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from './users.model';
import * as bcrypt from 'bcrypt';
@Controller('auth')
export class UsersController {
constructor(private readonly usersService: UsersService) { }
@Post('/signup')
async createUser(
@Body('password') password: string,
@Body('username') username: string,
): Promise<User> {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltOrRounds);
const result = await this.usersService.createUser(
username,
hashedPassword,
);
return result;
}
}
Здесь мы определили два маршрута API и использовали созданные нами сервисы.
Создание модуля авторизации
Давайте начнем с создания модуля аутентификации, например:
nest g module auth
Эта команда создаст новую папку auth
с файлом auth.module.ts
; он также обновит файл app.module.ts
.
Настройка JWT
Теперь давайте реализуем веб-токен JSON для аутентификации пользователей в приложении.
Для начала установите следующие зависимости:
npm install --save @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt
Затем создайте новый файл local.auth.ts
и добавьте следующий код:
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
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) {
throw new UnauthorizedException();
}
return user;
}
}
Здесь мы реализовали локальную стратегию паспорта для аутентификации веб-токена JSON. По умолчанию стратегия «Password-local» ожидает username
и password
свойства в теле запроса.
Мы также реализовали метод validate()
, который промежуточное ПО Passport будет вызывать для проверки пользователя с использованием соответствующего набора параметров, зависящего от стратегии.
Затем замените код AuthModule
на следующий:
import { Module } from "@nestjs/common"
import { UserModule } from "src/user/user.module";
import { AuthService } from "./auth.service"
import { PassportModule } from "@nestjs/passport"
import { JwtModule } from '@nestjs/jwt';
import { AuthController } from './auth.controller';
import { UsersService } from "src/user/user.service";
import { MongooseModule } from "@nestjs/mongoose"
import { UserSchema } from "../user/user.model"
import { LocalStrategy } from './local-strategy';
@Module({
imports: [UserModule, PassportModule, JwtModule.register({
secret: 'secretKey',
signOptions: { expiresIn: '60s' },
}), MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
providers: [AuthService, UsersService, LocalStrategy],
controllers: [AuthController],
})
export class AuthModule { }
Здесь мы импортировали PassportModule
и JwtModule
в массив импорта. Затем мы использовали этот register
метод для регистрации JWT, указав секрет и срок действия.
Мы также сделали UserSchema
доступными в импорте и добавили UserService
наши и LocalStrategy
в массив провайдеров.
из соображений безопасности всегда сохраняйте свой секрет JWT в переменной среды.
Создание службы аутентификации и контроллера
Теперь давайте добавим в приложение функции аутентификации.
С настроенными JWT и Passport выполните следующую команду, чтобы создать auth.service.ts
и auth.controller.ts
файлы в папке auth
:
nest generate service auth nest generate controller auth
Затем откройте файл auth/auth.service.ts
и аутентифицируйте пользователей с помощью следующего кода:
import { Injectable, NotAcceptableException } from '@nestjs/common';
import { UsersService } from 'src/user/user.service';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly usersService: UsersService, private jwtService: JwtService) { }
async validateUser(username: string, password: string): Promise<any> {
const user = await this.usersService.getUser({ username });
if (!user) return null;
const passwordValid = await bcrypt.compare(password, user.password)
if (!user) {
throw new NotAcceptableException('could not find the user');
}
if (user && passwordValid) {
return user;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user._id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
Здесь мы создали validateUser
метод, чтобы проверить, соответствует ли пользователь из записи пользователя из базы данных. Если совпадения нет, метод возвращает значение.user.modelnull
Мы также создали login
метод, который использует этот метод для создания токена доступа JWT для возвращенного пользователя из нашего файла .jwtService.signvalidateLocalStrategy
Теперь добавьте фрагмент кода ниже в файл auth/auth.controller.ts
, чтобы создать маршрут login
для пользователя .
import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '@nestjs/passport';
@Controller()
export class AuthController {
constructor(private authService: AuthService) { }
@UseGuards(AuthGuard('local'))
@Post('auth/login')
async login(@Request() req) {
return this.authService.login(req.user);
}
}
Здесь мы использовали @UseGuards()
декоратор для обеспечения аутентификации, когда пользователь запрашивает маршрут входа. С помощью класса AuthGuardlocal
мы можем аутентифицировать пользователя, используя стратегию.
Тестирование приложения
Теперь давайте протестируем приложение с помощью Postman. Мы начнем с signup route
.
Сначала запустите приложение:
npm run start
Затем откройте Postman и проверьте маршрут регистрации localhost:3000/users/signup
, отправив почтовый запрос на конечную точку.
Теперь проверьте конечную точку входа в систему, отправив почтовый запрос на конечную точку localhost:3000/auth/login
.
Если username
и password
существуют в базе данных, пользователь получит, access_token
как показано выше. С помощью access_token
пользователь сможет получить доступ к защищенным маршрутам в API.
Заключение
В этом руководстве мы представили обзор NestJS, а затем продемонстрировали, как реализовать аутентификацию пользователя JWT в NestJS API.