← Назад к вопросам

Как на проекте устроена авторизация?

1.3 Junior🔥 111 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура авторизации в веб-приложении

Авторизация (authentication) - это процесс проверки личности пользователя и выдачи ему токена для доступа к защищённым ресурсам. Существует множество подходов, и каждый имеет свои преимущества.

Основные методы авторизации

1. Session-based (Сессии)

Классический подход с cookies:

// Клиент отправляет логин/пароль
fetch('https://api.example.com/login', {
  method: 'POST',
  credentials: 'include',  // Отправить cookies
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'secret123'
  })
})
.then(res => res.json())
.then(data => {
  // Сервер возвращает Set-Cookie: sessionId=abc123
  // Браузер автоматически сохранит cookie
  // При следующих запросах cookie отправится автоматически
  console.log('Авторизирован');
});

// Следующий запрос
fetch('https://api.example.com/profile', {
  credentials: 'include'  // Отправить сохранённый cookie
})
// Сервер проверит sessionId из cookie

Поток session-based:

1. Клиент: POST /login -> отправляет логин/пароль
2. Сервер: проверяет пароль -> создаёт сессию -> возвращает Set-Cookie
3. Браузер: сохранит cookie
4. Клиент: GET /profile -> отправляет cookie автоматически
5. Сервер: проверяет сессию из cookie -> возвращает данные

Преимущества:

  • Простая реализация
  • CSRF защита встроена
  • Сессия хранится на сервере (безопасно)

Недостатки:

  • Масштабируемость (нужно синхронизировать сессии между серверами)
  • Мобильные приложения (cookies работают хуже)

2. Token-based (JWT)

Современный подход с токенами:

// Клиент отправляет логин/пароль
fetch('https://api.example.com/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'secret123'
  })
})
.then(res => res.json())
.then(data => {
  // Сервер возвращает JWT токен
  const token = data.access_token;
  // Клиент сохранит токен (в localStorage, sessionStorage, или memory)
  localStorage.setItem('token', token);
});

// Следующий запрос
fetch('https://api.example.com/profile', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('token')}`
  }
})
// Сервер проверит подпись токена

Структура JWT токена:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

// Декодировано:
// Header: {"alg":"HS256","typ":"JWT"}
// Payload: {"sub":"1234567890","name":"John Doe","iat":1516239022}
// Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Преимущества:

  • Масштабируемость (не нужна синхронизация на сервере)
  • Мобильные приложения
  • Микросервисная архитектура

Недостатки:

  • Токен нельзя отозвать быстро (до истечения)
  • XSS уязвимость (если хранить в localStorage)

OAuth 2.0 (Третьих сторон)

Делегированная авторизация через Google, GitHub, Facebook:

// Клиент перенаправляется на Google
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?` +
  `client_id=YOUR_CLIENT_ID&` +
  `redirect_uri=https://yourapp.com/callback&` +
  `response_type=code&` +
  `scope=email profile`;

// После ввода данных на Google,
// браузер перенаправляется на /callback?code=abc123

// На backend:
// POST https://oauth2.googleapis.com/token
// {
//   code: 'abc123',
//   client_id: 'YOUR_CLIENT_ID',
//   client_secret: 'YOUR_SECRET',
//   redirect_uri: 'https://yourapp.com/callback'
// }
// Возвращает access_token и refresh_token

Поток OAuth 2.0:

1. Клиент: нажимает "Sign in with Google"
2. Клиент: перенаправляется на google.com
3. Google: пользователь вводит пароль
4. Google: перенаправляет на ваше приложение с кодом
5. Сервер: обменивает код на токен (скрытно)
6. Сервер: возвращает ваш JWT или sessionId
7. Клиент: сохраняет токен

Типичная архитектура для Frontend

Компонент авторизации:

// AuthContext.tsx
import { createContext, useState, useEffect } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [token, setToken] = useState(null);

  // Проверить токен при загрузке
  useEffect(() => {
    const savedToken = localStorage.getItem('token');
    if (savedToken) {
      validateToken(savedToken);
    }
    setLoading(false);
  }, []);

  const validateToken = async (token) => {
    try {
      const res = await fetch('/api/profile', {
        headers: { 'Authorization': `Bearer ${token}` }
      });
      const data = await res.json();
      setUser(data);
      setToken(token);
    } catch (error) {
      localStorage.removeItem('token');
    }
  };

  const login = async (email, password) => {
    const res = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    });
    const data = await res.json();
    localStorage.setItem('token', data.access_token);
    setToken(data.access_token);
    setUser(data.user);
  };

  const logout = () => {
    localStorage.removeItem('token');
    setToken(null);
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, token, login, logout, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

// useAuth.ts
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}

Защищённый роут:

// ProtectedRoute.tsx
import { useAuth } from '@/hooks/useAuth';
import { Navigate } from 'react-router-dom';

export function ProtectedRoute({ children }) {
  const { user, loading } = useAuth();

  if (loading) return <div>Loading...</div>;
  if (!user) return <Navigate to="/login" />;

  return children;
}

// App.tsx
<Routes>
  <Route path="/login" element={<LoginPage />} />
  <Route path="/profile" element={
    <ProtectedRoute>
      <ProfilePage />
    </ProtectedRoute>
  } />
</Routes>

Интерцептор запросов:

// api.ts
const api = axios.create({
  baseURL: 'https://api.example.com'
});

// Добавить токен к каждому запросу
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Обновить токен если истёк
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      // Токен истёк
      const refreshToken = localStorage.getItem('refresh_token');
      const res = await fetch('/api/refresh', {
        method: 'POST',
        body: JSON.stringify({ refresh_token: refreshToken })
      });
      const data = await res.json();
      localStorage.setItem('token', data.access_token);
      // Повторить оригинальный запрос
      return api.request(error.config);
    }
    return Promise.reject(error);
  }
);

export default api;

Безопасность при авторизации

Хранение токенов:

  • localStorage - уязвим для XSS
  • sessionStorage - уязвим для XSS
  • HTTP-only cookies - защищён от XSS, но нужна CSRF защита
  • Memory - теряется при перезагрузке

Best practice:

// Хранить токен в памяти
// Refresh token в HTTP-only cookie
// При перезагрузке обновить токен с помощью refresh token

HTTPS:

// ВСЕГДА использовать HTTPS для авторизации
// Токены в заголовках легко перехватываются по HTTP

CORS:

// POST запросы к auth endpoints должны иметь CORS headers
fetch('https://api.example.com/login', {
  method: 'POST',
  credentials: 'include', // Если используются cookies
  headers: { 'Content-Type': 'application/json' }
});

Заключение

Авторизация - критичный компонент приложения. Главные подходы:

  • Sessions - для монолитных приложений
  • JWT - для современных микросервисов и мобильных
  • OAuth 2.0 - для интеграции с третьими сторонами

Независимо от выбранного метода, приоритет - безопасность и удобство пользователя.

Как на проекте устроена авторизация? | PrepBro