Настройка авторизации через OAUTH2 сервера https://oauth2.volmed.org.ru: различия между версиями
Перейти к навигации
Перейти к поиску
Misha (обсуждение | вклад) |
Misha (обсуждение | вклад) |
||
| (не показаны 33 промежуточные версии этого же участника) | |||
| Строка 5: | Строка 5: | ||
# Добавляем в настроечный файл сервиса следующие строки | # Добавляем в настроечный файл сервиса следующие строки | ||
<pre> | <pre> | ||
$GLOBALS['id_resource'] = 7; // Номер WEB интерфейса | $GLOBALS['id_resource'] = 7; // Номер WEB интерфейса из таблицы oauth_serv.oauth_client.client_id | ||
$ | $GLOBALS['oauth_client_secret'] = 'sh%we&&3#(jHg&jgrbn'; // Код из таблицы oauth_serv.oauth_client.client_secret | ||
$GLOBALS['jwt_key'] = 'sdklfwiomwefwepiojwepjowfmwfmwef'; // Строка с набором символов для шифрования JWT токена | $GLOBALS['jwt_key'] = 'sdklfwiomwefwepiojwepjowfmwfmwef'; // Строка с набором символов для шифрования JWT токена | ||
$oauth2_url = 'https://oauth2.volmed.org.ru'; // URL сервиса авторизации | $oauth2_url = 'https://oauth2.volmed.org.ru'; // URL сервиса авторизации | ||
| Строка 16: | Строка 15: | ||
</pre> | </pre> | ||
#Для работы с JWT токеном используем библиотеку https://github.com/firebase/php-jwt. Ее нужно установить в каталог class/jwt | #Для работы с JWT токеном используем библиотеку https://github.com/firebase/php-jwt. Ее нужно установить в каталог class/jwt | ||
== | ===Создание таблицы для хранения дополнительных данных=== | ||
Создаем таблицу | |||
<pre> | <pre> | ||
CREATE TABLE `user_sessions` ( | |||
`session_id` VARCHAR(64) NOT NULL COMMENT 'уникальная строка, хранится в JWT и БД' COLLATE 'utf8mb4_0900_ai_ci', | |||
`user_id` INT NOT NULL COMMENT 'идентификатор пользователя', | |||
`ip` VARCHAR(45) NULL DEFAULT NULL COMMENT 'опционально, помогает выявлять кражу сессии' COLLATE 'utf8mb4_0900_ai_ci', | |||
`user_agent_hash` VARCHAR(64) NULL DEFAULT NULL COMMENT 'опционально, помогает выявлять кражу сессии' COLLATE 'utf8mb4_0900_ai_ci', | |||
`created_at` DATETIME NULL DEFAULT NULL COMMENT 'время создания сессии', | |||
`last_activity` DATETIME NULL DEFAULT NULL, | |||
PRIMARY KEY (`session_id`) USING BTREE | |||
) | |||
COLLATE='utf8mb4_0900_ai_ci' | |||
ENGINE=InnoDB | |||
; | |||
</pre> | |||
==Класс для авторизации по Aouth2== | |||
<pre> | |||
use Firebase\JWT\JWT; | |||
use Firebase\JWT\Key; | |||
class AuthSait | |||
{ | { | ||
/* | /* | ||
* Класс по авторизации на сайт | |||
*/ | */ | ||
public array $auth = []; | |||
public array $picture; | |||
private string $ua_hash = ''; | |||
private int $jwt_ttl = 3600; // 1 час время жизни JVT токена | |||
private string $jwt_algo = 'HS256'; | |||
private $secure; | |||
private int $sess_id_live = 4800; // 8 часов Время жизни сессии. | |||
public function __construct() | |||
{ | |||
// Проверяем, что есть объект работы с БД | |||
if (!empty($GLOBALS['db']) && is_object($GLOBALS['db'])) { | |||
} else { | |||
return 'Нет подключения к БД сайта'; | |||
} | |||
// Проверяем, что есть объект работы с БД пользователей | |||
if (!empty($GLOBALS['lpu_user']) && is_object($GLOBALS['lpu_user'])) { | |||
} else { | |||
return 'Нет подключения к БД пользователей'; | |||
} | |||
$this->secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); | |||
$this->ua_hash = hash('sha256', $_SERVER['HTTP_USER_AGENT']); // Создаем hash от User_Agent | |||
// Переменная с путями до картинок пользователей по умолчанию, в зависимости от пола | |||
$this->picture = [ | |||
0 => '/users/pictures/user-m.png', | |||
1 => '/users/pictures/user-m.png', | |||
2 => '/users/pictures/user-w.png', | |||
]; | |||
} | |||
} | } | ||
</pre> | |||
===Основной метод авторизации=== | |||
<pre> | <pre> | ||
public function auth() | |||
{ | |||
// printr($_COOKIE); | |||
session_start(); | |||
$page = $_GET['page'] ?? ''; | |||
// Logout пользователя | |||
if ($page === 'logout') { | |||
$this->auth['logout'] = $this->logout(1); | |||
return $this->auth; | |||
} | |||
// 1️⃣ Проверяем JWT cookie | |||
if (!empty($_COOKIE['jwt'])) { | |||
$this->test_cookie(); | |||
return $this->auth; | |||
} | |||
// 2️⃣ Если возвращаемся с сервера OAuth2 с code | |||
if (!empty($_GET['code'])) { | |||
$return = $this->test_code(); | |||
header("Location: $return"); | |||
exit; | |||
} | |||
// Если это первый вход, то переходим на страницу Авторизации | |||
$url = $this->oauth2_login(); | |||
header("Location: $url"); | |||
exit; | |||
} | |||
</pre> | |||
===Проверка пользователя=== | |||
<pre> | <pre> | ||
private function test_cookie() | |||
{ | { | ||
// Проверка авторизации и создание переменной авторизации $auth | |||
* | try { | ||
$this->load_cookie(); | |||
$this->load_data_user(); | |||
$this->role_list(); | |||
} catch (\Exception $e) { | |||
// JWT невалиден → удаляем cookie | |||
$this->logout(1); | |||
} | |||
} | } | ||
</pre> | |||
===Метод выхода из системы=== | |||
<pre> | |||
public function logout($num) | |||
{ | |||
/* | |||
* Выход из системы | |||
*/ | |||
// 2️⃣ Удаляем сессию на сервере (если есть) | |||
if (!empty($_COOKIE['jwt'])) { | |||
try { | |||
$payload = \Firebase\JWT\JWT::decode($_COOKIE['jwt'], new \Firebase\JWT\Key($GLOBALS['jwt_key'], $this->jwt_algo)); | |||
if (!empty($payload->session_id)) { | |||
$GLOBALS['db']->mysqli_qw( | |||
'DELETE FROM user_sessions WHERE session_id = ?', | |||
$payload->session_id | |||
); | |||
} | |||
} catch (\Exception $e) { | |||
// JWT невалиден → ничего не делаем | |||
} | |||
} | |||
if (!empty($this->auth['error']) && !empty($this->auth['text_error'])) { | |||
$this->auth['error'] .= $this->auth['text_error']; | |||
} | |||
// printr($this->auth); | |||
foreach ($_COOKIE as $key => $val) { | |||
if (!empty($_COOKIE[$key])) { | |||
unset($_COOKIE[$key]); | |||
setcookie($key, "", time() - 3600, '/'); | |||
} | |||
} | |||
$this->auth = []; | |||
$this->oauth2_logout(); | |||
$logout = 1; | |||
return $logout; | |||
} | |||
</pre> | |||
===Метод выхода из OAUTH2 сервера=== | |||
<pre> | |||
protected function oauth2_logout() | |||
{ | |||
/* | |||
* Выход из системы через oauth2 | |||
*/ | |||
// echo "oauth2_logout<br>"; | |||
$url_arr = [ | |||
'logout' => 1, | |||
'redirect_uri' => (empty($_SERVER['HTTPS']) ? 'http' : 'https') . "://$_SERVER[HTTP_HOST]", | |||
]; | |||
$url = $GLOBALS['oauth2_url'] . '?' . http_build_query($url_arr); | |||
header('Location: ' . $url); | |||
exit(); | |||
} | |||
</pre> | |||
===Проверка авторизации и создания $auth=== | |||
<pre> | |||
private function test_cookie() | |||
{ | |||
// Проверка авторизации и создание переменной авторизации $auth | |||
try { | |||
$this->load_cookie(); | |||
$this->load_data_user(); | |||
$this->role_list(); | |||
} catch (\Exception $e) { | |||
// JWT невалиден → удаляем cookie | |||
$this->logout(1); | |||
} | |||
} | |||
</pre> | |||
===Метод по проверке COOKIE и загрузки их в переменные для проверки авторизации=== | |||
<pre> | |||
protected function load_cookie() | |||
{ | |||
/* | |||
* Метод по проверке COOKIE и загрузки их в переменные для проверки авторизации | |||
*/ | |||
$payload = \Firebase\JWT\JWT::decode( | |||
$_COOKIE['jwt'], | |||
new \Firebase\JWT\Key($GLOBALS['jwt_key'], $this->jwt_algo) | |||
); | |||
if (!empty($payload->session_id) && !empty($payload->id_user)) { | |||
// Проверяем сессию на сервере | |||
$res = $GLOBALS['db']->mysqli_qw( | |||
'SELECT session_id, user_id | |||
FROM user_sessions | |||
WHERE session_id = ? AND user_id = ? | |||
AND user_agent_hash=? AND last_activity > DATE_SUB(NOW(), INTERVAL ? MINUTE)', | |||
$payload->session_id, | |||
$payload->id_user, | |||
$this->ua_hash, | |||
$this->sess_id_live | |||
); | |||
$session = mysqli_fetch_assoc($res); | |||
if ($session) { | |||
// обновляем last_activity | |||
$GLOBALS['db']->mysqli_qw( | |||
'UPDATE user_sessions SET last_activity = NOW() WHERE session_id = ?', | |||
$payload->session_id | |||
); | |||
// обновляем JWT если прошло больше половины жизни | |||
if ($payload->iat < time() - ($this->jwt_ttl / 2)) { | |||
$new_payload = [ | |||
'id_user' => $payload->id_user, | |||
'session_id' => $payload->session_id, | |||
'iat' => time(), | |||
'exp' => time() + $this->jwt_ttl | |||
]; | |||
$this->set_cookie($new_payload, $this->jwt_ttl); | |||
} | |||
// возвращаем авторизацию | |||
$this->auth = [ | |||
'id' => $payload->id_user, | |||
]; | |||
} else { | |||
// Сессия протухла - Пользователь не работал более $sess_id_live минут | |||
$this->logout(1); | |||
exit; | |||
} | |||
} | |||
} | |||
</pre> | |||
===Если возвращаемся с сервера OAuth2 с code=== | |||
<pre> | <pre> | ||
protected function test_code() | |||
{ | |||
if (empty($_GET['state']) || $_GET['state'] !== ($_SESSION['state'] ?? '')) { | |||
die('Ошибка авторизации: invalid state'); | |||
} | |||
== | $oauth2_access = $this->oauth2_post($_GET['code']); // получает access_token и id_user | ||
if (empty($oauth2_access['access_token']) || empty($oauth2_access['id_user'])) { | |||
die('OAuth авторизация не удалась'); | |||
} | |||
// Проверяем, есть ли уже активная сессия для этого пользователя и UA | |||
$existing = $GLOBALS['db']->mysqli_qw( | |||
'SELECT session_id FROM user_sessions | |||
WHERE user_id=? | |||
AND user_agent_hash=? | |||
AND last_activity > DATE_SUB(NOW(), INTERVAL ? MINUTE)', | |||
$oauth2_access['id_user'], | |||
$this->ua_hash, | |||
$this->sess_id_live | |||
); | |||
$row = mysqli_fetch_assoc($existing); | |||
if ($row) { | |||
$session_id = $row['session_id']; | |||
} else { | |||
// создаём новую сессию | |||
$session_id = bin2hex(random_bytes(16)); | |||
$GLOBALS['db']->mysqli_qw( | |||
'INSERT INTO user_sessions (session_id, user_id, user_agent_hash, created_at, last_activity) | |||
VALUES (?, ?, ?, NOW(), NOW())', | |||
$session_id, | |||
$oauth2_access['id_user'], | |||
$this->ua_hash | |||
); | |||
} | |||
// создаём JWT | |||
$new_payload = [ | |||
'id_user' => $oauth2_access['id_user'], | |||
'session_id' => $session_id, | |||
'iat' => time(), | |||
'exp' => time() + $this->jwt_ttl | |||
]; | |||
$this->set_cookie($new_payload); | |||
// редиректим на исходную страницу | |||
$url = $_SESSION['oauth_return_to'] ?? '/'; | |||
unset($_SESSION['state'], $_SESSION['oauth_return_to']); | |||
return $url; | |||
} | |||
</pre> | |||
===Если это начало авторизации=== | |||
<pre> | <pre> | ||
private function oauth2_login() | |||
{ | |||
// 3️⃣ Начинаем OAuth, если нет cookie и нет code | |||
$state = bin2hex(random_bytes(16)); | |||
$_SESSION['state'] = $state; | |||
$_SESSION['oauth_return_to'] = $_SERVER['REQUEST_URI']; | |||
$url_data = [ | |||
'response_type' => 'code', | |||
'client_id' => $GLOBALS['id_resource'], | |||
'state' => $state, | |||
'redirect_uri' => $GLOBALS['redirect_uri'] | |||
]; | |||
$url = $GLOBALS['oauth2_url'] . '?' . http_build_query($url_data); | |||
return $url; | |||
} | |||
} | |||
</pre> | </pre> | ||
===Если получили code с Aouth2 сервера и запрашиваем данные авторизации=== | |||
== | |||
<pre> | <pre> | ||
private function oauth2_post() | private function oauth2_post() | ||
{ | { | ||
$post_data = [ | |||
"user_id" => $_GET['user_id'], | |||
"code" => $_GET['code'], | |||
'grant_type' => 'authorization_code', | |||
'client_id' => $GLOBALS['id_resource'], // Код должен совпадать с oauth2_db.oauth_client.client_id | |||
'client_secret' => $GLOBALS['oauth_client_secret'], // Код должен совпадать с oauth2_db.oauth_client.client_secret | |||
'redirect_uri' => $GLOBALS['redirect_uri'], | |||
// 'scope' => 'read write', | |||
]; | ]; | ||
// printr($post_data); | |||
$data_string = json_encode($post_data); | |||
$ch = curl_init(); | |||
curl_setopt($ch, CURLOPT_URL, $GLOBALS['oauth2_url'] . '/token'); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); | |||
curl_setopt($ch, CURLOPT_POST, true); | |||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string); | |||
curl_setopt($ch, CURLOPT_HTTPHEADER, array( | |||
'Content-Type: application/json', | |||
'Content-Length: ' . strlen($data_string), | |||
)); | |||
$oauth2_access = []; | |||
$output = curl_exec($ch); | |||
// echo "output=$output<br>"; | |||
// execute the request | |||
if ($output === false) echo 'Ошибка curl: ' . curl_error($ch); | |||
else { | |||
$oauth2_access = json_decode($output, true); | |||
// printr($oauth2_access); | |||
if (!empty($oauth2_access['error'])) { | |||
printr($oauth2_access); | |||
exit(); | |||
} | |||
$oauth2_access['id_user'] = $_GET['user_id']; | |||
} | |||
curl_close($ch); | curl_close($ch); | ||
// exit(); | |||
return $oauth2_access; | return $oauth2_access; | ||
} | } | ||
</pre> | |||
===Метод по установке COOKIE=== | |||
<pre> | |||
protected function set_cookie($new_payload) | |||
{ | |||
/* | |||
* Устанавливаем COOKIE | |||
*/ | |||
setcookie( | |||
'jwt', | |||
\Firebase\JWT\JWT::encode($new_payload, $GLOBALS['jwt_key'], $this->jwt_algo), | |||
[ | |||
'expires' => time() + $this->jwt_ttl, | |||
'path' => '/', | |||
'secure' => $this->secure, | |||
'httponly' => true, | |||
'samesite' => 'Lax' | |||
] | |||
); | |||
} | |||
</pre> | |||
===Осталные методы=== | |||
Методы load_data_user() и role_list() не связаны с OAUTH2 авторизацией. | |||
#load_data_user() - вытаскивает из базы данные оператора | |||
#role_list() - вытаскивает роли этого оператора. | |||
==Запрос пользовательских данных== | |||
Так же пользовательские данные можно запросить методом с сервера OAUTH2.<br> | |||
Но надо учесть проблему: Тк access_token через короткое время протухает, то нужно эти данные сразу куда то сохранять. И если есть возможность, как у меня вытащить эти данные прямо из БД, то лучше сделать так. | |||
<pre> | |||
public function take_userinfo() | |||
{ | |||
$accessToken = $this->access_token; // <-- access token | |||
$userInfoUrl = $GLOBALS['oauth2_url'] . '/userinfo'; // <-- endpoint | |||
$ch = curl_init(); | |||
curl_setopt($ch, CURLOPT_URL, $userInfoUrl); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
curl_setopt($ch, CURLOPT_HTTPHEADER, [ | |||
'Authorization: Bearer ' . $accessToken, | |||
'Accept: application/json' | |||
]); | |||
$response = curl_exec($ch); | |||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); | |||
if (curl_errno($ch)) { | |||
echo 'Ошибка cURL: ' . curl_error($ch); | |||
} elseif ($httpCode !== 200) { | |||
echo "Ошибка HTTP $httpCode: $response"; | |||
} else { | |||
$userData = json_decode($response, true); | |||
//printr($userData); | |||
} | |||
curl_close($ch); | |||
return $userData; | |||
} | |||
</pre> | </pre> | ||
Текущая версия от 14:52, 11 марта 2026
Подготовительные операции
- Идем в БД oauth_db на сервере 172.16.130.31 и добавляем в таблицу oauth_clients
- client_id - Номер WEB интерфейса, который вы собираетесь подключить
- client_secret - Набор любых символов для шифрования
- Добавляем в настроечный файл сервиса следующие строки
$GLOBALS['id_resource'] = 7; // Номер WEB интерфейса из таблицы oauth_serv.oauth_client.client_id $GLOBALS['oauth_client_secret'] = 'sh%we&&3#(jHg&jgrbn'; // Код из таблицы oauth_serv.oauth_client.client_secret $GLOBALS['jwt_key'] = 'sdklfwiomwefwepiojwepjowfmwfmwef'; // Строка с набором символов для шифрования JWT токена $oauth2_url = 'https://oauth2.volmed.org.ru'; // URL сервиса авторизации $GLOBALS['oauth2_url'] = $oauth2_url; $redirect_uri = $http."://$_SERVER[HTTP_HOST]"; $GLOBALS['redirect_uri'] = $redirect_uri;
- Для работы с JWT токеном используем библиотеку https://github.com/firebase/php-jwt. Ее нужно установить в каталог class/jwt
Создание таблицы для хранения дополнительных данных
Создаем таблицу
CREATE TABLE `user_sessions` ( `session_id` VARCHAR(64) NOT NULL COMMENT 'уникальная строка, хранится в JWT и БД' COLLATE 'utf8mb4_0900_ai_ci', `user_id` INT NOT NULL COMMENT 'идентификатор пользователя', `ip` VARCHAR(45) NULL DEFAULT NULL COMMENT 'опционально, помогает выявлять кражу сессии' COLLATE 'utf8mb4_0900_ai_ci', `user_agent_hash` VARCHAR(64) NULL DEFAULT NULL COMMENT 'опционально, помогает выявлять кражу сессии' COLLATE 'utf8mb4_0900_ai_ci', `created_at` DATETIME NULL DEFAULT NULL COMMENT 'время создания сессии', `last_activity` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`session_id`) USING BTREE ) COLLATE='utf8mb4_0900_ai_ci' ENGINE=InnoDB ;
Класс для авторизации по Aouth2
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class AuthSait
{
/*
* Класс по авторизации на сайт
*/
public array $auth = [];
public array $picture;
private string $ua_hash = '';
private int $jwt_ttl = 3600; // 1 час время жизни JVT токена
private string $jwt_algo = 'HS256';
private $secure;
private int $sess_id_live = 4800; // 8 часов Время жизни сессии.
public function __construct()
{
// Проверяем, что есть объект работы с БД
if (!empty($GLOBALS['db']) && is_object($GLOBALS['db'])) {
} else {
return 'Нет подключения к БД сайта';
}
// Проверяем, что есть объект работы с БД пользователей
if (!empty($GLOBALS['lpu_user']) && is_object($GLOBALS['lpu_user'])) {
} else {
return 'Нет подключения к БД пользователей';
}
$this->secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
$this->ua_hash = hash('sha256', $_SERVER['HTTP_USER_AGENT']); // Создаем hash от User_Agent
// Переменная с путями до картинок пользователей по умолчанию, в зависимости от пола
$this->picture = [
0 => '/users/pictures/user-m.png',
1 => '/users/pictures/user-m.png',
2 => '/users/pictures/user-w.png',
];
}
}
Основной метод авторизации
public function auth()
{
// printr($_COOKIE);
session_start();
$page = $_GET['page'] ?? '';
// Logout пользователя
if ($page === 'logout') {
$this->auth['logout'] = $this->logout(1);
return $this->auth;
}
// 1️⃣ Проверяем JWT cookie
if (!empty($_COOKIE['jwt'])) {
$this->test_cookie();
return $this->auth;
}
// 2️⃣ Если возвращаемся с сервера OAuth2 с code
if (!empty($_GET['code'])) {
$return = $this->test_code();
header("Location: $return");
exit;
}
// Если это первый вход, то переходим на страницу Авторизации
$url = $this->oauth2_login();
header("Location: $url");
exit;
}
Проверка пользователя
private function test_cookie()
{
// Проверка авторизации и создание переменной авторизации $auth
try {
$this->load_cookie();
$this->load_data_user();
$this->role_list();
} catch (\Exception $e) {
// JWT невалиден → удаляем cookie
$this->logout(1);
}
}
Метод выхода из системы
public function logout($num)
{
/*
* Выход из системы
*/
// 2️⃣ Удаляем сессию на сервере (если есть)
if (!empty($_COOKIE['jwt'])) {
try {
$payload = \Firebase\JWT\JWT::decode($_COOKIE['jwt'], new \Firebase\JWT\Key($GLOBALS['jwt_key'], $this->jwt_algo));
if (!empty($payload->session_id)) {
$GLOBALS['db']->mysqli_qw(
'DELETE FROM user_sessions WHERE session_id = ?',
$payload->session_id
);
}
} catch (\Exception $e) {
// JWT невалиден → ничего не делаем
}
}
if (!empty($this->auth['error']) && !empty($this->auth['text_error'])) {
$this->auth['error'] .= $this->auth['text_error'];
}
// printr($this->auth);
foreach ($_COOKIE as $key => $val) {
if (!empty($_COOKIE[$key])) {
unset($_COOKIE[$key]);
setcookie($key, "", time() - 3600, '/');
}
}
$this->auth = [];
$this->oauth2_logout();
$logout = 1;
return $logout;
}
Метод выхода из OAUTH2 сервера
protected function oauth2_logout()
{
/*
* Выход из системы через oauth2
*/
// echo "oauth2_logout<br>";
$url_arr = [
'logout' => 1,
'redirect_uri' => (empty($_SERVER['HTTPS']) ? 'http' : 'https') . "://$_SERVER[HTTP_HOST]",
];
$url = $GLOBALS['oauth2_url'] . '?' . http_build_query($url_arr);
header('Location: ' . $url);
exit();
}
Проверка авторизации и создания $auth
private function test_cookie()
{
// Проверка авторизации и создание переменной авторизации $auth
try {
$this->load_cookie();
$this->load_data_user();
$this->role_list();
} catch (\Exception $e) {
// JWT невалиден → удаляем cookie
$this->logout(1);
}
}
Метод по проверке COOKIE и загрузки их в переменные для проверки авторизации
protected function load_cookie()
{
/*
* Метод по проверке COOKIE и загрузки их в переменные для проверки авторизации
*/
$payload = \Firebase\JWT\JWT::decode(
$_COOKIE['jwt'],
new \Firebase\JWT\Key($GLOBALS['jwt_key'], $this->jwt_algo)
);
if (!empty($payload->session_id) && !empty($payload->id_user)) {
// Проверяем сессию на сервере
$res = $GLOBALS['db']->mysqli_qw(
'SELECT session_id, user_id
FROM user_sessions
WHERE session_id = ? AND user_id = ?
AND user_agent_hash=? AND last_activity > DATE_SUB(NOW(), INTERVAL ? MINUTE)',
$payload->session_id,
$payload->id_user,
$this->ua_hash,
$this->sess_id_live
);
$session = mysqli_fetch_assoc($res);
if ($session) {
// обновляем last_activity
$GLOBALS['db']->mysqli_qw(
'UPDATE user_sessions SET last_activity = NOW() WHERE session_id = ?',
$payload->session_id
);
// обновляем JWT если прошло больше половины жизни
if ($payload->iat < time() - ($this->jwt_ttl / 2)) {
$new_payload = [
'id_user' => $payload->id_user,
'session_id' => $payload->session_id,
'iat' => time(),
'exp' => time() + $this->jwt_ttl
];
$this->set_cookie($new_payload, $this->jwt_ttl);
}
// возвращаем авторизацию
$this->auth = [
'id' => $payload->id_user,
];
} else {
// Сессия протухла - Пользователь не работал более $sess_id_live минут
$this->logout(1);
exit;
}
}
}
Если возвращаемся с сервера OAuth2 с code
protected function test_code()
{
if (empty($_GET['state']) || $_GET['state'] !== ($_SESSION['state'] ?? '')) {
die('Ошибка авторизации: invalid state');
}
$oauth2_access = $this->oauth2_post($_GET['code']); // получает access_token и id_user
if (empty($oauth2_access['access_token']) || empty($oauth2_access['id_user'])) {
die('OAuth авторизация не удалась');
}
// Проверяем, есть ли уже активная сессия для этого пользователя и UA
$existing = $GLOBALS['db']->mysqli_qw(
'SELECT session_id FROM user_sessions
WHERE user_id=?
AND user_agent_hash=?
AND last_activity > DATE_SUB(NOW(), INTERVAL ? MINUTE)',
$oauth2_access['id_user'],
$this->ua_hash,
$this->sess_id_live
);
$row = mysqli_fetch_assoc($existing);
if ($row) {
$session_id = $row['session_id'];
} else {
// создаём новую сессию
$session_id = bin2hex(random_bytes(16));
$GLOBALS['db']->mysqli_qw(
'INSERT INTO user_sessions (session_id, user_id, user_agent_hash, created_at, last_activity)
VALUES (?, ?, ?, NOW(), NOW())',
$session_id,
$oauth2_access['id_user'],
$this->ua_hash
);
}
// создаём JWT
$new_payload = [
'id_user' => $oauth2_access['id_user'],
'session_id' => $session_id,
'iat' => time(),
'exp' => time() + $this->jwt_ttl
];
$this->set_cookie($new_payload);
// редиректим на исходную страницу
$url = $_SESSION['oauth_return_to'] ?? '/';
unset($_SESSION['state'], $_SESSION['oauth_return_to']);
return $url;
}
Если это начало авторизации
private function oauth2_login()
{
// 3️⃣ Начинаем OAuth, если нет cookie и нет code
$state = bin2hex(random_bytes(16));
$_SESSION['state'] = $state;
$_SESSION['oauth_return_to'] = $_SERVER['REQUEST_URI'];
$url_data = [
'response_type' => 'code',
'client_id' => $GLOBALS['id_resource'],
'state' => $state,
'redirect_uri' => $GLOBALS['redirect_uri']
];
$url = $GLOBALS['oauth2_url'] . '?' . http_build_query($url_data);
return $url;
}
Если получили code с Aouth2 сервера и запрашиваем данные авторизации
private function oauth2_post()
{
$post_data = [
"user_id" => $_GET['user_id'],
"code" => $_GET['code'],
'grant_type' => 'authorization_code',
'client_id' => $GLOBALS['id_resource'], // Код должен совпадать с oauth2_db.oauth_client.client_id
'client_secret' => $GLOBALS['oauth_client_secret'], // Код должен совпадать с oauth2_db.oauth_client.client_secret
'redirect_uri' => $GLOBALS['redirect_uri'],
// 'scope' => 'read write',
];
// printr($post_data);
$data_string = json_encode($post_data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $GLOBALS['oauth2_url'] . '/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string),
));
$oauth2_access = [];
$output = curl_exec($ch);
// echo "output=$output<br>";
// execute the request
if ($output === false) echo 'Ошибка curl: ' . curl_error($ch);
else {
$oauth2_access = json_decode($output, true);
// printr($oauth2_access);
if (!empty($oauth2_access['error'])) {
printr($oauth2_access);
exit();
}
$oauth2_access['id_user'] = $_GET['user_id'];
}
curl_close($ch);
// exit();
return $oauth2_access;
}
Метод по установке COOKIE
protected function set_cookie($new_payload)
{
/*
* Устанавливаем COOKIE
*/
setcookie(
'jwt',
\Firebase\JWT\JWT::encode($new_payload, $GLOBALS['jwt_key'], $this->jwt_algo),
[
'expires' => time() + $this->jwt_ttl,
'path' => '/',
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Lax'
]
);
}
Осталные методы
Методы load_data_user() и role_list() не связаны с OAUTH2 авторизацией.
- load_data_user() - вытаскивает из базы данные оператора
- role_list() - вытаскивает роли этого оператора.
Запрос пользовательских данных
Так же пользовательские данные можно запросить методом с сервера OAUTH2.
Но надо учесть проблему: Тк access_token через короткое время протухает, то нужно эти данные сразу куда то сохранять. И если есть возможность, как у меня вытащить эти данные прямо из БД, то лучше сделать так.
public function take_userinfo()
{
$accessToken = $this->access_token; // <-- access token
$userInfoUrl = $GLOBALS['oauth2_url'] . '/userinfo'; // <-- endpoint
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $userInfoUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
'Accept: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
echo 'Ошибка cURL: ' . curl_error($ch);
} elseif ($httpCode !== 200) {
echo "Ошибка HTTP $httpCode: $response";
} else {
$userData = json_decode($response, true);
//printr($userData);
}
curl_close($ch);
return $userData;
}