Создание сервиса аутентификации в PHP: выбор подхода и реализация
Варианты реализации аутентификации в PHP
Как реализовать аутентификацию с помощью JWT в PHP?
JSON Web Token (JWT) обеспечивает stateless аутентификацию, подходящую для API и микросервисов. Токен содержит утверждения (claims) и подписывается секретом. PHP реализация часто использует библиотеку firebase/php-jwt.
// Установка библиотеки через composer
composer require firebase/php-jwt
// Генерация токена
$key = "secret_key";
$payload = [
"iss" => "example.com",
"aud" => "example.com",
"iat" => time(),
"exp" => time() + 3600,
"user_id" => 123
];
$jwt = JWT::encode($payload, $key, 'HS256');Admin index php login php (страница входа администратора php)
Пояснения:
- iss - издатель, aud - аудитория, iat - время выпуска, exp - время истечения.
- user_id - идентификатор пользователя.
Типичные ошибки: не использовать bcrypt для паролей, не проверять срок действия токена, неправильная обработка секретного ключа (хранение в коде). Рекомендуется хранить ключ в .env и использовать только HTTPS.
Как организовать аутентификацию через сессии в PHP?
Сессионная аутентификация подходит для традиционных веб-приложений. PHP автоматически управляет сессиями через файлы или базу данных. После проверки пароля (с помощью password_verify) сохраняем идентификатор пользователя в $_SESSION.
session_start();
$password_hash = '$2y$10$...'; // из базы
if (password_verify($_POST['password'], $password_hash)) {
$_SESSION['user_id'] = $user['id'];
// регенерация сессии для предотвращения фиксации
session_regenerate_id(true);
}Php code login (код страницы входа php)
Ошибки: не вызывать session_regenerate_id после входа, незащищенные сессии от CSRF (нужно добавить CSRF токен), неправильное хранение паролей (использовать password_hash с bcrypt).
Как интегрировать вход через внешние сервисы (OAuth2)?
OAuth2 позволяет пользователям входить через Google, GitHub и др. Используйте библиотеку league/oauth2-client. Регистрируйте приложение, получайте client_id и client_secret, перенаправляйте на URL авторизации, обрабатывайте callback.
$provider = new League\OAuth2\Client\Provider\Google([
'clientId' => '...',
'clientSecret' => '...',
'redirectUri' => 'https://example.com/callback'
]);
$accessToken = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
$resourceOwner = $provider->getResourceOwner($accessToken);
$email = $resourceOwner->getEmail();Request login php (запрос на вход php)
Проблемы: неверный redirect URI, истечение токена, несоответствие scopes. Нужно хранить state для защиты от CSRF.
Как использовать готовые решения для аутентификации, например, Laravel Sanctum?
Laravel Sanctum предоставляет простую аутентификацию API через токены. Подходит для SPA и мобильных приложений. Sanctum использует сессии для SPA и простые токены для API.
// Установка в Laravel
composer require laravel/sanctum
// Выдача токена в контроллере
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$token = $user->createToken('api-token')->plainTextToken;Ошибки: не настроен Guard, не указаны middleware, неправильная конфигурация CORS. Следует учитывать, что Sanctum ориентирован на Laravel.
Расширенные примеры аутентификации на PHP
Пример 1. Полный цикл JWT с refresh токеном
В этом примере создается класс для работы с JWT, включая выпуск access и refresh токенов, их проверку и обновление. Refresh токен хранится в базе данных.
class AuthService {
private $key;
private $refreshKey;
public function __construct() {
$this->key = $_ENV['JWT_SECRET'];
$this->refreshKey = $_ENV['JWT_REFRESH_SECRET'];
}
public function generateTokens($userId) {
$accessPayload = [
'user_id' => $userId,
'exp' => time() + 3600, // 1 час
'type' => 'access'
];
$accessToken = JWT::encode($accessPayload, $this->key, 'HS256');
$refreshPayload = [
'user_id' => $userId,
'exp' => time() + 86400 * 30, // 30 дней
'type' => 'refresh'
];
$refreshToken = JWT::encode($refreshPayload, $this->refreshKey, 'HS256');
// Сохранение refresh токена в БД
DB::table('refresh_tokens')->insert([
'user_id' => $userId,
'token' => $refreshToken,
'expires_at' => date('Y-m-d H:i:s', time() + 86400*30)
]);
return ['access_token' => $accessToken, 'refresh_token' => $refreshToken];
}
public function refreshAccessToken($refreshToken) {
try {
$decoded = JWT::decode($refreshToken, $this->refreshKey, ['HS256']);
} catch (\Exception $e) {
return null; // невалидный refresh токен
}
// Проверка существования в БД
$stored = DB::table('refresh_tokens')
->where('token', $refreshToken)
->where('user_id', $decoded->user_id)
->first();
if (!$stored) return null;
// Удаление старого refresh токена
DB::table('refresh_tokens')->where('id', $stored->id)->delete();
// Создание новой пары токенов
return $this->generateTokens($decoded->user_id);
}
}// Пример использования (вывод JSON):
$authService = new AuthService();
$tokens = $authService->generateTokens(42);
echo json_encode($tokens);
// Результат:
// {"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
// "refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."}Типичные ошибки: не удалять старые refresh токены, не проверять срок действия refresh токена, отсутствие удаления при logout. Рекомендуется устанавливать короткий срок жизни access токена и длинный для refresh.
Пример 2. Интеграция Google OAuth2 с полным циклом
Используется библиотека league/oauth2-google. Регистрация приложения в Google Cloud, получение credentials. Затем реализуется маршрут для входа и callback.
// auth.php - начало входа
session_start();
$provider = new League\OAuth2\Client\Provider\Google([
'clientId' => getenv('GOOGLE_CLIENT_ID'),
'clientSecret' => getenv('GOOGLE_CLIENT_SECRET'),
'redirectUri' => 'https://example.com/callback'
]);
// Генерация state для защиты от CSRF
$state = bin2hex(random_bytes(16));
$_SESSION['oauth2state'] = $state;
$authorizationUrl = $provider->getAuthorizationUrl([
'scope' => ['email', 'profile'],
'state' => $state
]);
header('Location: ' . $authorizationUrl);
exit;
// callback.php - обработка ответа
session_start();
if (empty($_GET['state']) || (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
}
$token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
$owner = $provider->getResourceOwner($token);
echo 'Email: ' . $owner->getEmail();
echo 'Name: ' . $owner->getName();
// Далее создаем или находим пользователя в БД и аутентифицируем// После успешного входа браузер перенаправляется на /callback и выводит: Email: user@gmail.com Name: John Doe
Проблемы: несовпадение redirect_uri в настройках Google, неверный state (CSRF), отсутствие обработки ошибок при запросе токена. Необходимо проверять email domain для ограничения доступа.
Пример 3. Сессионная аутентификация с CSRF защитой
Реализация входа с паролем, регенерацией сессии и добавлением CSRF токена в форму.
// login.php - отображение формы
session_start();
$csrfToken = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrfToken;
?>
<form method="post" action="authenticate.php">
<input type="hidden" name="csrf_token" value="<?= $csrfToken ?>">
Email: <input type="email" name="email"><br>
Password: <input type="password" name="password"><br>
<button type="submit">Войти</button>
</form>
// authenticate.php - обработка
session_start();
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF token mismatch');
}
$user = User::findByEmail($_POST['email']);
if ($user && password_verify($_POST['password'], $user->password_hash)) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user->id;
header('Location: dashboard.php');
} else {
echo 'Неверные учетные данные';
}// При успешном входе происходит перенаправление на dashboard.php. При неверном CSRF токене - сообщение "CSRF token mismatch" и прерывание.
Ошибки: не удалять старый CSRF токен после использования, незащищенные от timing атак сравнения (использовать hash_equals), отсутствие регенерации сессии при каждой смене уровня привилегий. Рекомендуется также устанавливать HttpOnly и Secure флаги для cookie сессии.