Какова последовательность действий для получения JWT-токена при аутентификации?

Ответ

Процесс получения JWT (JSON Web Token) следует стандартному flow аутентификации:

Основные шаги:

  1. Клиент отправляет учетные данные.

    • Обычно POST-запрос на эндпоинт /login или /oauth/token.
    • Тело запроса содержит username/password, client_id/client_secret (для OAuth2) или refresh token.
      POST /api/auth/login HTTP/1.1
      Content-Type: application/json
      {
      "username": "user@example.com",
      "password": "secure_password"
      }
  2. Сервер аутентификации проверяет данные.

    • Валидирует логин/пароль в БД или через внешний провайдер (LDAP, OIDC).
    • Проверяет активность учетной записи и права доступа.
  3. Сервер генерирует и подписывает JWT.

    • Header: Определяет алгоритм подписи (например, HS256 или RS256).
    • Payload (Claims): Содержит данные (sub, roles, exp — время истечения, iat — время выдачи).
    • Signature: Создается путем подписи header.payload секретным ключом (HMAC) или приватным ключом (RSA).
  4. Сервер возвращает токен клиенту.

    • Чаще всего в теле ответа как JSON-объект.
      {
      "access_token": "eyJhbGciOiJIUzI1NiIs...",
      "token_type": "Bearer",
      "expires_in": 3600
      }

Пример на Spring Security (упрощенно):

// Конфигурация сервера (используя Password Grant)
@Configuration
@EnableAuthorizationServer
public class AuthConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("clientapp")
            .secret(passwordEncoder().encode("secret"))
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write");
    }
}
// Клиентский запрос (с помощью RestTemplate или WebClient)
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("clientapp", "secret");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "password");
body.add("username", "user");
body.add("password", "pass");

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(
    "http://auth-server/oauth/token",
    request,
    Map.class
);
String jwt = (String) response.getBody().get("access_token");

Best Practices:

  • Всегда используйте HTTPS.
  • Храните секретные ключи в безопасном хранилище (не в коде).
  • Устанавливайте разумное время жизни токена (expires_in).
  • Для хранения на клиенте предпочтительны HttpOnly, Secure куки.

Ответ 18+ 🔞

А, слушай, смотри, про эти ваши JWTшки, ну, это ж классика, блядь, как два пальца обоссать! Весь этот цирк с аутентификацией, он по одному и тому же сценарию, как по нотам, пиздец.

Ну, короче, вот как это обычно происходит, по шагам, чтобы даже мартышке понятно было:

  1. Клиент, этот, лезет, сука, со своими данными.

    • Тыкает POST-запросом куда-нибудь в /login или, там, в /oauth/token, если он такой весь из себя продвинутый.
    • И суёт в тело, понимаешь, либо логин с паролем, либо client_id со своим секретом, либо, если он уже не первый день живёт, refresh token свой потрёпанный.
      POST /api/auth/login HTTP/1.1
      Content-Type: application/json
      {
      "username": "user@example.com",
      "password": "secure_password"
      }
  2. Сервер аутентификации, этот важный бугай, начинает проверять.

    • Лезет в базу, смотрит: а есть ли такой юзер, и совпадает ли хэш пароля, а то мало ли, блядь, кто пришёл. Или там в LDAP стучится, или к OIDC-провайдеру.
    • Заодно глядит, не заблочили ли аккаунт и какие у него там права, чтобы не дай бог куда не надо полез.
  3. А вот тут, сука, начинается магия! Сервер генерирует токен.

    • Header (Заголовок): Тут пишут, какой алгоритм подписи, типа HS256 или RS256. Чтоб все знали, как проверять.
    • Payload (Данные, они же claims): А это самое вкусное! Сюда пихают всё: кто (sub), какие роли, когда истечёт (exp — это святое, блядь!), когда выпущен (iat).
    • Signature (Подпись): А это, блядь, чтобы не подделали! Берут header.payload, и ебут их секретным ключом (если HMAC) или приватным ключом (если RSA). Получается этакая криптографическая печать, в рот меня чих-пых!
  4. И сервер, довольный как слон, отдаёт токен обратно.

    • Обычно в JSON-объекте, чтоб красиво было.
      {
      "access_token": "eyJhbGciOiJIUzI1NiIs...",
      "token_type": "Bearer",
      "expires_in": 3600
      }

      Всё, клиент с этим токеном, как с гранатой, бегает по API.

Ну, а если на Spring Security, то вот, смотри, упрощённо, как это выглядит, ёпта:

// Конфигурация сервера (используя Password Grant)
@Configuration
@EnableAuthorizationServer
public class AuthConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("clientapp")
            .secret(passwordEncoder().encode("secret"))
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write");
    }
}
// Клиентский запрос (с помощью RestTemplate или WebClient)
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("clientapp", "secret");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "password");
body.add("username", "user");
body.add("password", "pass");

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(
    "http://auth-server/oauth/token",
    request,
    Map.class
);
String jwt = (String) response.getBody().get("access_token");

И главное, блядь, не накосячь с безопасностью, запомни раз и нахуй:

  • HTTPS — это не обсуждается, ёпта! Без него — ты просто раздаёшь свои данные всем подряд, как мудак.
  • Ключи, эти секретные, храни не в коде, а в нормальном хранилище (Vault, KMS), а то будет овердохуища стыда.
  • Время жизни токена (expires_in) ставь адекватное. Не на год, блядь, но и не на пять минут, чтобы пользователь не взбесился.
  • На клиенте лучше всего токен в HttpOnly, Secure куки пихать. В localStorage — это как оставлять ключи от квартиры под ковриком, пидарас шерстяной.