Как реализовать в nestjs авторизацию jwt с ролями доступа к API. Например student имеет доступ только просматривать оценки, а teacher имеет доступ их ставить. Для доступа к данным используем prisma

Реализация JWT авторизации с ролями в NestJS с использованием Prisma

В этом примере мы создадим систему авторизации с использованием JWT в NestJS, где роли (student и teacher) будут определять доступ к API. Мы будем использовать Prisma для работы с базой данных.


Шаг 1: Установка необходимых пакетов

Установите зависимости для работы с JWT:

bash

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt @prisma/client
npm install -D @types/passport-jwt


Шаг 2: Настройка Prisma модели

Добавьте модель пользователей с ролями в файл prisma/schema.prisma:

prisma

model User {
  id       Int      @id @default(autoincrement())
  email    String   @unique
  password String
  role     Role
}

enum Role {
  STUDENT
  TEACHER
}

После этого выполните миграцию:

bash

npx prisma migrate dev --name add_user_roles


Шаг 3: Создание модуля аутентификации

  1. Создайте модуль auth:bashnest generate module auth nest generate service auth nest generate controller auth
  2. Настройте auth.service.ts для обработки JWT и проверки пользователей:typescriptimport { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as bcrypt from 'bcrypt'; import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class AuthService { constructor(private jwtService: JwtService, private prisma: PrismaService) {} async validateUser(email: string, password: string) { const user = await this.prisma.user.findUnique({ where: { email } }); if (user && (await bcrypt.compare(password, user.password))) { const { password, ...result } = user; return result; } return null; } async login(user: any) { const payload = { email: user.email, sub: user.id, role: user.role }; return { access_token: this.jwtService.sign(payload), }; } }
  3. Настройте auth.controller.ts для маршрутов аутентификации:typescriptimport { Controller, Post, Body, Request, UseGuards } from '@nestjs/common'; import { AuthService } from './auth.service'; import { LocalAuthGuard } from './guards/local-auth.guard'; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @Post('login') @UseGuards(LocalAuthGuard) async login(@Request() req) { return this.authService.login(req.user); } }
  4. Настройте стратегию local для проверки логина:typescriptimport { 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({ usernameField: 'email' }); } async validate(email: string, password: string): Promise<any> { const user = await this.authService.validateUser(email, password); if (!user) { throw new UnauthorizedException(); } return user; } }
  5. Настройте стратегию jwt для защиты маршрутов:typescriptimport { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: 'your_jwt_secret', }); } async validate(payload: any) { return { userId: payload.sub, email: payload.email, role: payload.role }; } }

Шаг 4: Реализация ролей

  1. Создайте roles.guard.ts для проверки ролей:typescriptimport { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler()); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.includes(user.role); } }
  2. Создайте декоратор roles.decorator.ts:typescriptimport { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

Шаг 5: Пример использования ролей в контроллере

  1. Создайте контроллер для работы с оценками:bashnest generate controller grades
  2. Настройте маршруты в grades.controller.ts:typescriptimport { Controller, Get, Post, Body, UseGuards } from '@nestjs/common'; import { Roles } from '../auth/roles.decorator'; import { RolesGuard } from '../auth/guards/roles.guard'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; @Controller('grades') export class GradesController { @Get() @UseGuards(JwtAuthGuard, RolesGuard) @Roles('STUDENT', 'TEACHER') getGrades() { return 'Просмотр оценок'; } @Post() @UseGuards(JwtAuthGuard, RolesGuard) @Roles('TEACHER') setGrade(@Body() body: { studentId: number; grade: number }) { return `Оценка ${body.grade} поставлена студенту ${body.studentId}`; } }

Шаг 6: Тестирование

  1. Зарегистрируйте пользователей с ролями STUDENT и TEACHER в базе данных.
  2. Выполните вход через /auth/login, чтобы получить JWT токен.
  3. Используйте токен для доступа к маршрутам:
    • GET /grades: Доступен для STUDENT и TEACHER.
    • POST /grades: Доступен только для TEACHER.

Теперь у вас есть система авторизации с JWT и ролями в NestJS, интегрированная с Prisma!