JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Проверка авторизации в Middleware

Проверка авторизации в Middleware

Модуль 4: Node.js, Next.js и Angular
11 уровень , 6 лекция
Открыта

1. Введение

В любом современном веб-приложении есть защищённые страницы: личный кабинет, страница профиля, разделы для администраторов и т.д. Доступ к ним должен быть только у авторизованных пользователей. Если пользователь не авторизован — его нужно либо перенаправить на страницу входа, либо показать ошибку.

Проверять авторизацию можно прямо внутри компонентов или на сервере в API-обработчиках, но куда удобнее и надёжнее делать это централизованно — с помощью middleware. Тогда вы точно не забудете "прикрыть" нужный маршрут, и логика проверки будет в одном месте.

Напомним: файл middleware.ts (или middleware.js) лежит в корне проекта или в папке src/. Каждый раз, когда пользователь делает запрос к вашему приложению, этот файл выполняется до того, как страница или API-обработчик начнут работать.

Middleware получает объект запроса (Request) и может:

  • Пропустить запрос дальше (ничего не делать)
  • Прервать выполнение (вернуть ответ самому)
  • Сделать редирект (например, на /login)
  • Изменить заголовки, куки и т.д.

2. Как устроена авторизация в Next.js-приложениях?

В Next.js (особенно с использованием next-auth) авторизация чаще всего реализуется через куки: после входа пользователя на сервере создаётся специальная кука с сессионным токеном. Этот токен отправляется с каждым запросом, и по нему можно узнать — кто перед нами: гость или залогиненный пользователь.

Важный момент:
В middleware мы не можем использовать React-хуки или получать данные из Client Components, но можем работать с куками и заголовками запроса.

Пример: простая проверка наличия куки

Давайте рассмотрим базовый вариант: если у пользователя нет куки session-token — отправляем его на страницу входа.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Получаем куку из запроса
  const session = request.cookies.get('session-token')?.value

  // Если куки нет — редиректим на /login
  if (!session) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  // Если всё хорошо — пропускаем запрос дальше
  return NextResponse.next()
}

Объяснение:
Мы достаём значение куки с помощью request.cookies.get().
Если куки нет, возвращаем редирект на /login.
Если кука есть — пропускаем запрос дальше.

3. Проверка авторизации только для определённых маршрутов

Часто не нужно защищать всё приложение — только часть маршрутов (например, /dashboard, /profile, /admin). Как это сделать?

В Next.js middleware можно ограничить срабатывание через экспорт объекта config:


export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}

Теперь middleware будет работать только для этих маршрутов.

Полный пример:


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const session = request.cookies.get('session-token')?.value
  if (!session) {
    return NextResponse.redirect(new URL('/login', request.url))
  }
  return NextResponse.next()
}

// Ограничиваем срабатывание только на защищённых маршрутах
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}

4. Интеграция с next-auth: проверяем сессию

Если вы используете next-auth, то для проверки авторизации в middleware есть специальный способ — через функцию getToken из пакета next-auth/jwt. Она позволяет расшифровать токен прямо внутри middleware.

Установка (если ещё не установлено):

npm install next-auth

Пример:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'

export async function middleware(request: NextRequest) {
  // Получаем токен пользователя (если есть)
  const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })

  // Если токена нет — редиректим на /login
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  // Если токен есть — пропускаем
  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}

Объяснение:
getToken сам найдёт и расшифрует токен из куки.
Если токена нет — пользователь не авторизован.

5. Продвинутый пример: проверка роли пользователя

Допустим, у вас есть разные роли: обычный пользователь и админ. И вы хотите, чтобы на /admin допускались только админы.


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'

export async function middleware(request: NextRequest) {
  const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })

  // Если токена нет — редиректим на /login
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  // Проверяем: если пользователь НЕ админ и идёт на /admin — редиректим на главную
  if (
    request.nextUrl.pathname.startsWith('/admin') &&
    token.role !== 'admin'
  ) {
    return NextResponse.redirect(new URL('/', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}

Объяснение:
В объекте token можно хранить любые данные пользователя (например, роль).
Если роль не admin, а маршрут — /admin, делаем редирект на главную страницу.

6. Практика: защита страниц профиля и админки

Давайте добавим в наше учебное приложение защиту для маршрутов /profile и /admin.

  1. В файле middleware.ts реализуем проверку токена.
  2. Для /profile — достаточно просто быть авторизованным.
  3. Для /admin — нужна роль admin.

Код:


import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'

export async function middleware(request: NextRequest) {
  const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })

  if (!token) {
    // Не авторизован — всегда на /login
    return NextResponse.redirect(new URL('/login', request.url))
  }

  // Если идём на /admin, проверяем роль
  if (request.nextUrl.pathname.startsWith('/admin') && token.role !== 'admin') {
    // Не админ — на главную
    return NextResponse.redirect(new URL('/', request.url))
  }

  // Всё хорошо
  return NextResponse.next()
}

export const config = {
  matcher: ['/profile/:path*', '/admin/:path*']
}

7. Типичные ошибки при проверке авторизации в Middleware

Ошибка №1: Проверка авторизации на всех маршрутах подряд.
Если не ограничить срабатывание middleware через matcher, вы можете случайно заблокировать даже публичные страницы (например, /about, /login, /api/public). Это приведёт к "вечному редиректу" или невозможности попасть на страницу входа.

Ошибка №2: Не тот секрет для getToken.
Если вы забыли передать правильный secret в getToken, токен не расшифруется, и все пользователи будут считаться неавторизованными. Проверьте, что переменная окружения NEXTAUTH_SECRET совпадает с тем, что использует next-auth.

Ошибка №3: Проверка только наличия куки, а не её валидности.
Бывает, что кука есть, но она просрочена или подделана. Лучше использовать getToken или аналогичные методы, чем просто проверять наличие строки.

Ошибка №4: Использование fetch или сторонних сетевых запросов в middleware.
Middleware должен работать очень быстро и синхронно. Не делайте сетевых запросов к базе или API — это замедлит все переходы по сайту.

Ошибка №5: Редирект на тот же маршрут.
Если в middleware вы делаете редирект на ту же страницу (например, /login редиректит на /login), получите бесконечную петлю. Проверьте, что маршрут назначения отличается от текущего.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ