Ответ
Процесс аутентификации по схеме OAuth 2.0 с выдачей JWT-токена (поток Resource Owner Password Credentials или Authorization Code):
1. Запрос на получение токена: Клиентское приложение отправляет HTTP POST-запрос на эндпоинт аутентификации сервера авторизации (Authorization Server).
Пример тела запроса для потока Password Credentials:
POST /oauth/token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
grant_type=password&username=user&password=pass&scope=read write
2. Валидация учетных данных: Сервер авторизации:
- Проверяет
client_idиclient_secret(для confidential clients). - Аутентифицирует пользователя по
usernameиpassword(сверяя с БД, LDAP, etc.). - Проверяет запрошенные
scopeна соответствие правам клиента.
3. Генерация JWT-токена: Если валидация успешна, сервер создаёт JWT (JSON Web Token), состоящий из трёх частей:
- Header: Алгоритм подписи (например,
{"alg": "HS256", "typ": "JWT"}). - Payload (Claims): Данные (например,
{"sub": "123", "username": "john", "roles": ["USER"], "exp": 1678886400}). - Signature: Цифровая подпись, созданная с использованием секретного ключа (HMAC) или приватного ключа (RSA).
4. Отправка ответа клиенту: Сервер возвращает JSON-ответ с токеном и метаданными.
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200e2b4c...",
"scope": "read write"
}
5. Использование токена клиентом:
Клиент сохраняет токен (в памяти, localStorage или безопасных куках) и использует его для доступа к защищённым ресурсам (Resource Server), добавляя в заголовок:
GET /api/protected-resource HTTP/1.1
Host: api-server.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Пример кода на Spring Security (упрощённо):
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
// 1. Аутентификация
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
// 2. Генерация JWT
String jwt = jwtTokenUtil.generateToken(authentication);
// 3. Ответ
return ResponseEntity.ok(new AuthResponse(jwt));
}
}
Важно: Поток password рекомендуется только для доверенных клиентов (например, собственные мобильные приложения). Для веб-приложений предпочтительнее поток Authorization Code с PKCE.
Ответ 18+ 🔞
Ну вот, опять эти ваши OAuth и JWT, блядь. Сидишь такой, пьешь кофе, думаешь — ну щас всё просто, логин-пароль, и вперёд. А нихуя! Тут целый цирк с конями, ебать мои старые костыли.
Смотри, как эта шарманка крутится, если по-простому.
1. Начинается цирк — запрос на токен. Твоё приложение, как обоссанный щенок, бежит к серверу авторизации и стучится: «Пусти, я свой!». И шлёт он не абы что, а вот такую хуйню:
POST /oauth/token HTTP/1.1
Host: auth-server.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
grant_type=password&username=user&password=pass&scope=read write
Видишь этот grant_type=password? Это как сказать: «Эй, мужик, я доверенный, вот мой паспорт и пароль от жены, дай пропуск». Но так можно только с очень доверенными клиентами, а то сейчас налетят пидары и всё просрут.
2. Сервер начинает воротить нос. Он получает эту простыню и думает: «А не пиздят ли мне?». И начинает проверять:
client_idиclient_secret— эти ксивы левые или нет?usernameиpassword— а этот чел в базе есть или это опять Вася из соседнего подъезда пытается зайти?scope=read write— а этому клиенту вообще можно столько прав? Может, он только наreadпотянет?
3. Если всё чики-пуки, начинается магия — генерация JWT. Это не просто строка, блядь, это трёхслойный бутерброд, подписанный по всем правилам:
- Header (Шапка): Тут написано, какой алгоритм подписи. Типа
{"alg": "HS256", "typ": "JWT"}. Читай как «рубить буду так-то». - Payload (Начинка): А вот тут уже сочное. Кто юзер (
sub), как зовут (username), какие у него роли (roles), и главное — когда сдохнет этот токен (exp).{"sub": "123", "username": "john", "roles": ["USER"], "exp": 1678886400}. - Signature (Подпись): А это, сука, чтобы умники не подделали. Берётся шапка и начинка, склеиваются, и нахуй шифруются секретным ключом. Получается такая хитрая жопа, которую не взломать.
4. Выдача пропуска. Сервер, довольный собой, плюёт тебе в ответ JSON'ом:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200e2b4c...",
"scope": "read write"
}
Вот он, святой грааль! access_token — это твой пропуск на час (expires_in: 3600). refresh_token — это как запасная почка, если основной токен откинулся, можно им новый выбить.
5. И пошло-поехало.
Клиент хватает этот токен, суёт его в хранилище (только, бля, не в localStorage на виду, дурачок!), и теперь с ним можно ходить по всем защищённым API, как царь.
Просто добавляешь в заголовок:
GET /api/protected-resource HTTP/1.1
Host: api-server.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
И ресурсный сервер, увидев Bearer, говорит: «А, ну ладно, проходи».
А вот, блядь, как это выглядит внутри на Spring Security, если очень упрощённо:
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
// 1. Аутентификация (проверяем, не мудак ли)
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
// 2. Генерация JWT (если не мудак — печатаем пропуск)
String jwt = jwtTokenUtil.generateToken(authentication);
// 3. Ответ (получай и кайфуй)
return ResponseEntity.ok(new AuthResponse(jwt));
}
}
И главное, нахуй, запомни: этот поток password — он только для своих, для доверенных клиентов! Типа твоего родного мобильного приложения. Для всяких левых веб-приложений надо использовать Authorization Code с PKCE, это чтобы тебя не наебали по-крупному. А то придётся потом кричать «Муму!», как тот глухонемой Герасим, но уже ни хуя не поможет.