Как сделать токен авторизации

Добавил пользователь Дмитрий К.
Обновлено: 04.10.2024

code - это уникальный, одноразовый, временный ключ, который действует только 10 минут и с его помощью вам необходимо в течение этого времени получить access_token и refresh_token .

Обмен кода авторизации на access token и refresh token

Получив все параметры, вам необходимо сделать запрос на специальный адрес, описанный ниже. В ответ вы получите пару Access token и Refresh token.

Время жизни Access token - 2 часа. Дату и время создания токена, а также количество секундах, показывающее через сколько токен истечет, вы также получите в ответе.

Refresh token поможет вам автоматизировать получение нового Access token (убрать необходимость получения каждый раз нового кода авторизации в интерфейсе). Подробнее об этом в следующем параграфе.

Получение нового Access token по его истечении

При обмене кода авторизации на Access token вы получили и Refresh token. С его помощью вы можете получить новый Access token и продолжить работу с API.

Refresh token можно использовать только один раз. После отправки его в метод и получения новой пары Access token и Refresh token старый Refresh token становится не актуальным.

Запрос на обновление Access token по Refresh token такой же, как при обмене кода авторизации, но с другими параметрами и значениями.

В Spring Security JWT-токен используется в OAuth 2 как access token. Он выдается клиенту сервером авторизации, клиент отправляет его при обращении к серверу ресурсов. (А сервер ресурсов проверяет его, обращаясь к серверу авторизации).

Но этот пример — самописная реализация stateless REST API на JWT, единое приложение, которое и выдает, и проверяет токен. И выдает REST API.

На этом примере можно увидеть, как написать свой фильтр авторизации, как сформировать и проверить JWT-токен. Но в реальном проекте этот пример лучше не использовать.

Задача

В этой статье будет чистый REST-сервис без фронтенда. Подразумевается, что фронтенд написан отдельно: например, на каком-нибудь JavaScript-фрейворке.

В этом примере наш старый REST-контроллер останется, а настройка Spring Security не особо поменяется — скорее, она дополнится.

  1. Мы добавим в приложение конечную точку /authenticate для аутентификации. Сюда приходят имя и пароль от пользователя. Приложение проверяет пароль, и если он верный, высылает пользователю в ответ JWT-токен.
  2. Во всех дальнейших запросах пользователь обязан высылать в заголовке JWT-токен, наше приложение проверяет подлинность токена в специально написанном фильтре JWTFilter и, если он корректен, пропускает запрос дальше.

Подготовка

REST-контроллер

Итак, наш основной REST-контроллер остается прежним:

Он нужен для того, чтобы запретить к нему доступ и потом разрешить только авторизованным пользователям.

Аутентификация с пользовательским UserDetailsService

Настройка аутентификации такая:

Подробнее об аутентификации с UserDetailsService есть статья.

Если кратко, мы переопределяем метод loadUserByUsername(), чтобы Spring Security понимал, как взять пользователя по его имени из хранилища. Имея этот метод, SS может сравнить переданный пароль с настоящим и аутентифицировать пользователя (либо не аутентифицировать).

Пользователи хранятся в In-Memory базе данных H2. Работаем через Hibernate.

А данными заполняем базу на старте приложения с помощью data.sql (этот файл надо положить в папку /resources, а в application.yml включить его запуск):

А теперь перейдем собственно в JWT-токену.

Библиотека для работы с JWT-токеном

Для работы с JWT добавим Maven-зависимость:

Для работы с JWT-токеном уже написаны утилиты, так что используем готовую. В ней есть метод формирования токена, методы извлечения имени пользователя и других данных. Метод формирования токена изменим так, чтобы записывать в токен еще список authorities в виде клейма authorities.

Во всех методах извлечения данных JWT-токен заодно проверяется на предмет не истек ли он, и валидна ли подпись.

Эти методы утилиты нам пригодятся:

Это была подготовка. Перейдем, наконец, к написанию своего кода, касающегося JWT.

Конечная точка аутентификации для выдачи JWT-токена

Тут собственно выдается JWT-токен. Пользователь делает POST-запрос с именем и паролем по адресу /authenticate, а в ответ получает сгенерированынй токен. Токен генерится методом generateToken() из утилиты выше.

Запрос имеет такой формат:

Фронтенд сохраняет у себя JWT-токен, и потом использует его при каждом запросе.

Если пользователь хочет выйти, токен должен быть уничтожен на фронтенде. На бэкенде (в нашем Spring приложении) он продолжит действовать до истечения своего срока. А вообще теоретически можно сделать черный список токенов, но не в этом примере.

Еще: чтобы сделать токены сразу всех пользователей недействительными, достаточно поменять секретный код. Но тогда разлогинены будут все сразу.

В этом проблема JWT-токена — нужны обходные пути для того, чтобы сделать его недействительным.

Перейдем ко второй принципиальной части — фильтру, проверяющему токен при каждом запросе.

Фильтр, проверяющий JWT-токен при каждом запросе

Итак, JWT-токен выдан, клиент его нам отправляет при каждом запросе, надо этот токен при каждом запросе проверять (и извлекать из него имя пользователя). Для этого напишем фильтр JWTFilter. Он расширяет OncePerRequestFilter и происходит в нем следующее:

  1. При каждом запросе из заголовка Authorization берем JWT-токен (он начинается с префикса «Bearer«).
  2. Извлекаем из него имя пользователя (claim subject) и список authorities (claim authorities). Оба клейма мы записывали в токен при его генерации в контроллере.
  3. Одновременно при извлечении claims проверяется валидность токена. Для этого не надо делать никаких запросов в базу: достаточно самого токена и jwt.secret (прописанного в application.yml). На основе этого секрета токен генерился, и на основе него он потом каждый раз проверяется с помощью хеш-функции (это делает библиотека jjwt).
  4. Если все ок, то имея имя пользователя и список authorities (извлеченные в п.2), создаем объект Authentication (точнее, его подкласс UsernamePasswordAuthenticationToken). И устанавливаем объект Authentication в SecurityContext. Так нужно для Spring Security.
  5. Если с токеном не все ок, то в п. 2-3 выбросился исключение, и фильтр не пропустит запрос в контроллер к защищенному /url.

Настройка авторизации: собираем все вместе

Тут все как раньше, но только:

  1. отключаем csrf,
  2. отключаем сессии
  3. и добавляем наш фильтр JWTFilter перед фильтром UsernamePasswordAuthenticationFilter.

Проверка

Запустим приложении и убедимся, что все работает.

Получение JWT-токена

Отправим POST-запрос нужного формата по адресу /authencate:

Этим запросом получаем jwt-токен

Этим запросом получаем jwt-токен

Отправка запроса с JWT-токеном

Токен получен, теперь с ним можно попасть на защищенную страницу. Для этого добавим его к запросу /user в заголовок Authorization.

При этом выберем тип Bearer-токен — это значит, что префикс Bearer будет добавлен к токену.

А теперь с ним запрашиваем защищенный url /user

А теперь с ним запрашиваем защищенный url /user

Как видно, страница /user получена.

Вид JWT-токена


Декодированный токен

После генерации токена вы можете сразу начать его использование для получения данных в API.

Сгенерированный токен ничем не отличается от токена, полученного по протоколу OAuth.

OAuth 2. Общая схема работы

Если вы разрабатываете приложение (бот для телеграма, плагин для общедоступной CRM и т.д.), которое может быть полезно для любого пользователя Модульбанка, вы можете авторизовывать пользователей через сервер авторизации Модульбанка по OAuth подобному протоколу. Схема авторизации пользователей идентична протоколу OAuth 2.0, за небольшим исключением что сервер авторизации Модульбанка кроме формата x-www-form-urlencoded также поддерживает формат json в теле запроса. Для того чтобы стороннее приложение могло совершать запросы к API от лица конкретного пользователя, приложению необходимо получить токен авторизации, подтверждающий что пользователь предоставил приложению права на выполнение тех или иных действий.

Схема получения токена авторизации пользователя сторонним приложением.

Запрос авторизации

Приложение отправляет запрос авторизации на сервер Модульбанка. Запрос авторизации отправляется из браузера клиента.

Параметры Тип Описание
code string Временный токен (authorization code), подлежащий обмену на постоянный токен авторизации. Присутствует если пользователь подтвердил авторизацию приложения
error string Код ошибки. Присутствует в случае ошибки или отказа в авторизации пользователем
description string Дополнительное текстовое пояснение ошибки
state string Транслируется из метода выше, если был передан
Значение поля error Описание
invalid_request В запросе отсутствуют обязательные параметры, либо параметры имеют некорректные или недопустимые значения.
invalid_scope Параметр scope отсутствует, либо имеет некорректное значение или имеет логические противоречия.
unauthorized_client Неверное значение параметра client_id, либо приложение заблокировано
access_denied Пользователь отклонил запрос авторизации приложения.

Пример ответа при успешной авторизации:

Ответ при отказе в авторизации:

Обмен временного токена на токен авторизации

Параметры Тип Описание
code string Временный токен (authorization code)
clientId string Идентификатор приложения, полученный при регистрации
clientSecret string Секретное слово для проверки подлинности приложения

В ответ на запрос сервер Модульбанка возвращает токен авторизации (accessToken), который является симметричным секретом приложения и дает право проводить операции со счетом пользователя. Токен возвращается в виде JSON-документа, который (в зависимости от результата обмена) может содержать одно из следующих полей:

Параметры Тип Описание
accessToken string Токен авторизации. Присутствует в случае успеха
error string Код ошибки. Присутствует в случае ошибки
Значение поля error Описание
invalid_request Обязательные параметры запроса отсутствуют или имеют некорректные или недопустимые значения
unauthorized_client Неверное значение параметра client_id или client_secret, либо приложение заблокировано
invalid_grant В выдаче access_token отказано. Временный токен не выдавался Модульбанком, либо просрочен, либо по этому временному токену уже выдан access_token (повторный запрос токена авторизации с тем же временным токеном)

Пример ответа при успешном обмене временного токена:

Пример ответа при ошибке:

Использовать временный токен можно только один раз. Если приложению не удалось получить ответ сервера за время жизни временного токена, процедуру авторизации следует повторить сначала. Внимание! Полученный accessToken является симметричным секретом авторизации, поэтому разработчик приложения должен предпринять меры по его защите: хранить токен в зашифрованном виде, предоставлять доступ к нему только после авторизации пользователя в приложении. Срок действия токена 3 года. По истечении этого времени, токен автоматически аннулируется.

Отзыв токена

Приложение может отозвать полученный токен авторизации. Для отзыва токена отправьте следующий запрос на сервер авторизации:

Пример успешного ответа:

Права на выполнение операций

При запросе авторизации стороннее приложение должно явно указывать в параметре scope набор прав, необходимых приложению для работы.

Без программирования, в несколько кликов - простой и быстрый способ трехногой авторизации по протоколу OAuth 2.0 в Google APIs. Получение refresh и первого access token для использования в HTTP-запросах из 1С к API Google. Для приложений типа "Компьютеры".

При использовании некоторых API Google (например Sheets API, Calendar API, Drive API) - в HTTP-запросах необходимо указывать заголовок "Authorization:Bearer ". Когда я знакомился с Google Sheets API, то отметил, что уйдет далеко не пара минут на изучение и программирование процесса получения токена, который позволяет обратиться к этому API. Причем refresh token выдается раз и практически навсегда. Получается, нужно тратить время на программирование того, что понадобится один раз. Предлагаю быстрый способ получения refresh и первого access токенов вообще без программирования. Работает для приложений типа "Компьютеры".

Можете попробовать прямо здесь. Нажмите "Get code", войдите/выберите свой аккаунт гугл, разрешите доступ, получите код, вставьте полученный код в зеленое поле "code:" и нажмите "Get tokens". Браузер покажет необходимые токены.

Если у Вас есть свой Идентификатор клиента OAuth 2.0 типа "Компьютеры" (что у Вас есть - смотрите в https://console.developers.google.com/apis/credentials), то можете вставить сюда свои scope, client_id (в 2 поля), client_secret - и получить свои токены. Или создайте новые учетные данные с типом "Приложение для ПК" по этой же ссылке, определитесь с нужными API и scope - и пробуйте.

Вкратце на этом всё, процесс закончен.

Но если пробовать здесь не хотите - можете создать у себя текстовый файл с расширением html следующего содержания:

Сохраняем html-файл (я назвал его GoogleOauth.html), открываем его в интернет-браузере.


Первым делом жмем кнопку "Get code". Если в браузере Вы еще не входили в аккаунт гугл - предложат войти. Если входили - откроется страница выбора аккаунта. К данным этого аккаунта мы предоставляем доступ приложению:


Кликаем аккаунт, отвечаем "Разрешить" на вопросы, откроется экран согласия (consent screen):


Жмем кнопку "Разрешить" - попадаем на страницу с кодом:


Копируем полученный код, возвращаемся к странице с нашим файлом GoogleOauth.html, вставляем в зеленое поле "code:" полученный код:


Наконец, нажимаем кнопку "Get tokens". В итоге браузер перейдет на страницу, где будет выведен текст JSON следующего содержания:


В результате мы получили "вечный" refresh_token и временный access_token, которые далее используем в запросах к API Google.

В примере выше для демонстрации по умолчанию подставляются client_id, client_secret и минимальные scope от моей тестовой учетки. Свою учетку Вы можете создать в консоли разработчика https://console.developers.google.com/apis/credentials , вверху страницы кликните "+ СОЗДАТЬ УЧЕТНЫЕ ДАННЫЕ" - "Идентификатор клиента OAuth" - тип приложения "Приложение для ПК".


После создания гугл выдаст client_id и client_secret - их и нужно вставить в браузер, в рассмотренную выше форму GoogleOauth.html Обратите внимание, client_id нужно вставить два раза в два одноименных поля. После вставки своих client_id client_secret а также необходимых Вам scope - производим все клики и получаем рабочие токены.

Немного о scope, которые мы видим в форме. Здесь нужно указать API-scope, необходимые для Вашего приложения. Перечень существующих scope можно посмотреть на странице

Читайте также: