Рубрики
Без рубрики

Проверка подлинности Spring Security Oauth2- JWT на сервере ресурсов

Oauth2 – это стандартный отраслевой протокол авторизации. В соответствии со спецификацией Oauth2 (RFC-6749)… Помеченный как spring, java, all.

Oauth2 – это стандартный отраслевой протокол авторизации.

В соответствии со спецификацией Oauth2 ( RFC-6749 ) —

Платформа авторизации OAuth 2.0 позволяет стороннему приложению получать ограниченный доступ к службе HTTP либо от имени владельца ресурса путем организации взаимодействия с утверждением между владельцем ресурса и службой HTTP, либо позволяя стороннему приложению получать доступ от своего имени.

Следующая диаграмма иллюстрирует работу запроса аутентификации Oauth2 с потоком предоставления кода авторизации .

В этом деле участвуют четыре стороны —

  • Клиент – это стороннее приложение, которое хочет получить доступ к защищенному ресурсу с сервера ресурсов.
  • Сервер аутентификации – это сервер, который помогает аутентифицировать владельца ресурса.
  • Владелец ресурса – это пользователь, которому принадлежит защищенный ресурс.
  • Сервер ресурсов – это сервер, который обслуживает защищенные ресурсы, принадлежащие Владельцу ресурса.
  1. Прежде всего, клиент отправляет запрос на авторизацию Владельцу ресурса, чтобы от имени Владельца ресурса он мог получить доступ к защищенному ресурсу (ресурсам).
  2. Если используется Authorization-code-grant , код авторизации возвращается клиенту. Это означает, что владелец ресурса предоставил клиенту доступ к защищенным ресурсам.
  3. Клиент отправляет этот код авторизации на Сервер аутентификации, который в свою очередь предоставляет токен аутентификации — обычно токен JWT.
  4. Как только у клиента есть токен аутентификации, он использует его для доступа к защищенным ресурсам с сервера ресурсов. Токен истекает по истечении установленного времени ожидания.

В этом посте мы сосредоточимся на 4-м шаге т.е. Как сервер ресурсов проверяет токен JWT, предоставленный любым сторонним клиентом .

Сначала давайте разберемся, что такое JWT и какие API предоставляет spring security для реализации аутентификации Jwt.

Что такое JWT?

👉🏼 Ознакомьтесь с полным введением по адресу/| jwt.io 😜

Spring Security API для аутентификации JWT

На приведенной ниже диаграмме представлен подробный обзор спецификаций Spring security API для аутентификации JWT.

  • Когда клиент отправляет запрос вместе с токеном на предъявителя. Он проходит через цепочку фильтров безопасности. Фильтр проверки подлинности токена на предъявителя создает BearerTokenAuthenticationToken типа Идентификация .

  • Затем решатель Authentication Manager разрешает Authentication Manager , который, в свою очередь, выбирает конкретный AuthenticationProvider .

  • Токен аутентификации Токен-носитель передается в Поставщик аутентификации с помощью ProviderManager ( ( реализация по умолчанию AuthenticationManager )

  • Для проверки подлинности JWT выбирается Поставщик проверки подлинности Jwt . Он декодирует , проверяет и проверяет | Jwt с помощью JwtDecoder . Если аутентификация прошла успешно, аутентификация устанавливается в

  • SecurityContextHolder . Если проверка подлинности завершается неудачно,

  • SecurityContextHolder очищается.

Наконец, давайте продолжим реализацию аутентификации JWT.

Аутентификация JWT в Spring Security

Для его реализации нам потребуются следующие компоненты —

  • Сервер аутентификации – мы будем использовать Keycloak . Он поддерживает Oauth2.0.
  • Сервер ресурсов – Мы создадим его с помощью приложения spring-boot.
  • Клиент – Мы можем использовать Postman API client в качестве клиента.
  • User – мы настроим одного пользователя на сервере Keycloak.

Сервер аутентификации с помощью Keycloak

Пока вы этим занимаетесь, вот несколько вещей, которые вам понадобятся после настройки сервера Keycloak.

Обязательно замените Обязательно замените с действительным доменом. Также обязательно укажите значение value для область

Как только у вас будет готов сервер Keycloak , давайте продолжим и создадим сервер ресурсов.

Сервер ресурсов

Сервер ресурсов будет самым простым и будет содержать только один безопасный rest API.

Зависимости:

  • spring-security-oauth2-ресурс-сервер ** — Здесь собрана большая часть поддержки сервера ресурсов.
  • spring-security-oauth2-jose — обеспечивает поддержку декодирования и проверки JWT.

        org.springframework.boot
        spring-boot-starter-web



        org.springframework.boot
        spring-boot-starter-oauth2-resource-server


        org.springframework.security
        spring-security-oauth2-jose


Конечная точка API

GET/api/v1/пользователи

@RestController
@RequestMapping("/api/v1")
public class UserController {

    @GetMapping("/users")
    public List getUsers(){
        return Arrays.asList(
                new User("john doe", 100),
                new User("jane doe",300)
        );
    }
}

Поскольку у нас есть Spring security в пути к классу, каждый маршрут будет частным.

Настройка URL-адреса эмитента JWT

Это минимальная настройка, необходимая для реализации аутентификации JWT. Предоставленный url-адрес эмитента используется Сервером ресурсов для обнаружения открытых ключей сервера авторизации и проверки токена. Это также тот же URL-адрес, который присутствует в iss претензия.

Spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-url: http://localhost:8080/auth/realms/{realm}

Когда сервер ресурсов запускается , он автоматически настраивается для проверки токена на предъявителя в кодировке JWT. Он достигает этого, запрашивая Сервер авторизации конечную точку метаданных для свойства jwks_url . Это обеспечивает доступ к поддерживаемому алгоритму и его действительным открытым ключам.

Единственным недостатком этой настройки является то, что она завершится ошибкой, если сервер аутентификации еще не запущен. Чтобы избежать сбоя при запуске, нам нужно было бы добавить jwk-set-uri

Spring:
    security:
        oauth2:
            resourceserver:
                jwt:
                    issuer-url: http://localhost:8080/auth/realms/dev
                    jwk-set-uri: http://localhost:8080/auth/realms/{realm}/protocol/openid-connect/certs

Теперь сервер ресурсов не будет пинговать сервер авторизации при запуске. Однако issueruri по-прежнему сохраняется для проверки утверждения JWT iss для входящего токена.

Spring Security предоставляет точки расширения для переопределения или настройки поведения реализации по умолчанию. Мы рассмотрим возможность настройки некоторых функций по умолчанию .

… Но перед этим давайте протестируем реализацию по умолчанию.

Время для тестирования реализации 💎

Если вы помните, сервер ресурсов содержит одну конечную точку с путем /api/v1/users . Если мы вызовем его без предоставления токена аутентификации, он вернет 401 - Неавторизованный статус. Это связано с отсутствием токена авторизации.

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

Шаг 1: Запрос кода авторизации OAuth

На этом этапе нам понадобится клиент, чтобы запросить код авторизации. Однако, чтобы упростить тестирование, мы можем запустить следующий URL-адрес в браузере. Он должен перенаправить вас на страницу входа в систему, и вам нужно будет предоставить учетные данные пользователя.

http://authserver.com/auth/realms/{realm}/protocol/openid-connect/auth
?client_id=your-client-id&response_type=code&state=app-state

После успешного завершения он перенаправит вас на redirect-url со значениями, аналогичными приведенным ниже.

http://{redirect-url}/?state=appstate
&session_state=d7c5d4de-c883-494a-a2a2-e5108062830c
&code=f5935f66-88e0-4085-80aa-000b2a6b2b51.d7c5d4de-c883-494a-a2a2-e5108062830c.bf89a5ff-5703-42d6-9534-ca59f667f81f

Нам потребуется code для извлечения фактического токена.

Шаг 2: Извлеките аутентификацию Знак

curl -L -X POST "http://localhost:8080/auth/realms/dev/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Cookie: JSESSIONID=E8D36F0DBCBF7E33130B9125F8795CAC.9b51ecd0cc5c; JSESSIONID=8E34666DECDB395B1754FD08C5B385F2" \
--data-urlencode "client_id=client-id-value" \
--data-urlencode "client_secret=client-secret-uuid" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=92236b97-c48f-4827-ae80-80a46e39a0f2.d7c5d4de-c883-494a-a2a2-e5108062830c.bf89a5ff-5703-42d6-9534-ca59f667f81f" \
--data-urlencode "redirect-uri=http://localhost:8085"

  • идентификатор клиента , секрет клиента могут быть получены из учетных данных клиента на сервере Keycloak .
  • grant_type описывает используемый тип гранта.
  • код извлечен на шаге 1.
  • url-адрес перенаправления должен быть таким же, как настроен для клиента в Keycloak server.

Возвращенный ответ будет выглядеть аналогично приведенному ниже примеру:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0S1ZQNGVhRVdjdno4ZjRndXlPd05XTE9GWFEzYWo0b1I0eWx0dkFSZldFIn0.eyJleHAiOjE2MzI5MzMzOTksImlhdCI6MTYzMjkzMzA5OSwiYXV0aF90aW1lIjoxNjMyOTMzMDgyLCJqdGkiOiIzZTk1NGRkMi0zMjhhLTQ3NzItYWQ2NS0xOWQ3NGM2MGZjZGEiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvZGV2IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImMzNjFkMGVjLWM1MWItNGRmNy05MTA1LTVhOWUyZGViMmRjOCIsInR5cCI6IkJlYXJlciIsImF6cCI6InJlc291cmNlc2VydmVyIiwic2Vzc2lvbl9zdGF0ZSI6ImMyYWRiODIyLWQwY2ItNDY3MC1iYmNjLWU3NWJlOTkyYzg5OSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1kZXYiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicmVzb3VyY2VzZXJ2ZXIiOnsicm9sZXMiOlsiVVNFUiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiYzJhZGI4MjItZDBjYi00NjcwLWJiY2MtZTc1YmU5OTJjODk5IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX25hbWUiOiJ1c2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidXNlciJ9.A66uqbRwsUL36GSGozZ7FC3x-M4SCYYLaABMdps-XneseP1saIjsTbHO2QrYq2HbD9jl6nKTYxJHjMdbsRJyY3VtM2mf1D8W24-u8y8qmGf1YNbtFfSTZyrUmwiACEv17onAT8wKgR0C4sdbVFETpRY12f2qQb0mM4ZkT9QQ5DYPBu6dnwyBVXLYJzn8kfmp7JB0OR6LsBTTtyh03t_xiRwb1nSALbUmwq7iUk9lTFEUuUZ182p05q3TKxy9b_kxrCh91EYoYWUdBEhRM4yHjrvN99T-MFpRVaCadyn2YibFbCeZHpsqUmgi-ghR3I70U70HGsL22FEAE4N9X5y_pg",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzZGU1NjBjZi1kMDZlLTRiZmItODY2Yi1mNzJhYjk0YjA0NGMifQ.eyJleHAiOjE2MzI5MzQ4OTksImlhdCI6MTYzMjkzMzA5OSwianRpIjoiODQ5OWNmY2QtZjY1Yy00YzdhLThhNDctMzdhNjg4ZGZjMjU0IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2RldiIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9kZXYiLCJzdWIiOiJjMzYxZDBlYy1jNTFiLTRkZjctOTEwNS01YTllMmRlYjJkYzgiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoicmVzb3VyY2VzZXJ2ZXIiLCJzZXNzaW9uX3N0YXRlIjoiYzJhZGI4MjItZDBjYi00NjcwLWJiY2MtZTc1YmU5OTJjODk5Iiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiYzJhZGI4MjItZDBjYi00NjcwLWJiY2MtZTc1YmU5OTJjODk5In0.747XKhyNqZCDEzSfLV4K96sgAW0daN1C1ROUr5L_s_E",
    "token_type": "Bearer",
    "not-before-policy": 0,
    "session_state": "c2adb822-d0cb-4670-bbcc-e75be992c899",
    "scope": "email profile"
}

Шаг 3: Запустите API с помощью

Мы будем использовать access_token значение из предыдущего ответа в качестве токена—носителя для запуска частного API – ( api/v1/users ), предоставляемого сервером ресурсов.

curl -L -X GET "http://localhost:8090/api/v1/users" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0S1ZQNGVhRVdjdno4ZjRndXlPd05XTE9GWFEzYWo0b1I0eWx0dkFSZldFIn0.eyJleHAiOjE2MzI5MzMzOTksImlhdCI6MTYzMjkzMzA5OSwiYXV0aF90aW1lIjoxNjMyOTMzMDgyLCJqdGkiOiIzZTk1NGRkMi0zMjhhLTQ3NzItYWQ2NS0xOWQ3NGM2MGZjZGEiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvZGV2IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImMzNjFkMGVjLWM1MWItNGRmNy05MTA1LTVhOWUyZGViMmRjOCIsInR5cCI6IkJlYXJlciIsImF6cCI6InJlc291cmNlc2VydmVyIiwic2Vzc2lvbl9zdGF0ZSI6ImMyYWRiODIyLWQwY2ItNDY3MC1iYmNjLWU3NWJlOTkyYzg5OSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1kZXYiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicmVzb3VyY2VzZXJ2ZXIiOnsicm9sZXMiOlsiVVNFUiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiYzJhZGI4MjItZDBjYi00NjcwLWJiY2MtZTc1YmU5OTJjODk5IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX25hbWUiOiJ1c2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidXNlciJ9.A66uqbRwsUL36GSGozZ7FC3x-M4SCYYLaABMdps-XneseP1saIjsTbHO2QrYq2HbD9jl6nKTYxJHjMdbsRJyY3VtM2mf1D8W24-u8y8qmGf1YNbtFfSTZyrUmwiACEv17onAT8wKgR0C4sdbVFETpRY12f2qQb0mM4ZkT9QQ5DYPBu6dnwyBVXLYJzn8kfmp7JB0OR6LsBTTtyh03t_xiRwb1nSALbUmwq7iUk9lTFEUuUZ182p05q3TKxy9b_kxrCh91EYoYWUdBEhRM4yHjrvN99T-MFpRVaCadyn2YibFbCeZHpsqUmgi-ghR3I70U70HGsL22FEAE4N9X5y_pg" \
-H "Cookie: JSESSIONID=8E34666DECDB395B1754FD08C5B385F2"

Ответ:

[
    {
        "name": "John Doe",
        "age": 100
    },
    {
        "name": "Jane Doe",
        "age": 300
    }
]

Следующими шагами в этой реализации являются настройка параметров по умолчанию, таких как

  • Добавлено другое значение тайм-аута, чтобы избежать каких-либо проблем в распределенных системах.
  • Настройка сопоставления предоставленных полномочий
  • .. и многое другое.

Я написал подробное руководство по этому поводу. Вы можете проверить это здесь

Оригинал: “https://dev.to/irshsheik/spring-security-oauth2-jwt-authentication-in-a-resource-server-1gk6”