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

Весенняя безопасность и подключение OpenID

Узнайте, как настроить OpenID Connect (от Google) с помощью простого приложения Spring Security.

Автор оригинала: Ger Roza.

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

1. Обзор

В этом быстром учебнике мы сосредоточимся на настройке OpenID Connect (OIDC) с Spring Security.

Мы представлю различные аспекты этой спецификации, а затем увидим поддержку, которую предлагает Spring Security для ее реализации на OAuth 2.0 Client.

2. Быстрый OpenID Подключение Введение

Подключение OpenID это слой идентификации, построенный поверх протокола OAuth 2.0.

Таким образом, это действительно важно знать, OAuth 2.0 перед погружением в OIDC, особенно поток Кода авторизации.

Набор спецификаций OIDC обширен; она включает в себя основные функции и ряд других дополнительных возможностей, представленных в различных группах. Основными из них являются:

  • Ядро: аутентификация и использование претензий для передачи информации конечного пользователя
  • Открытие: оговаривается, как клиент может динамически определять информацию о поставщиках OpenID
  • Динамическая регистрация: диктует, как клиент может зарегистрироваться у поставщика
  • Управление сеансами: определяет, как управлять сеансами OIDC

Вдобавок к этому, документы отличают OAuth 2.0 Серверы аутентификации, которые предлагают поддержку для этой спецификации, ссылаясь на них как “OpenID провайдеров” (OPs) и OAuth 2.0 Клиенты, которые используют OIDC как опираясь сторон (RPs). Мы будем придерживаться этой терминологии в этой статье.

Стоит также знать, что клиент может запросить использование этого расширения, добавив openid сферы в своем запросе на авторизацию.

Наконец, еще один аспект, который полезно понять для этого учебника является тот факт, что OPs излучают информацию о конечных пользователей, как JWT называется “ID Token”.

Теперь да, мы готовы погрузиться глубже в мир OIDC.

3. Настройка проекта

Прежде чем сосредоточиться на фактической разработке, мы должны зарегистрировать OAuth 2.o Клиента с нашим поставщиком OpenID.

В этом случае мы будем использовать Google в качестве поставщика OpenID. Мы можем следить за эти инструкции зарегистрировать наше клиентское приложение на своей платформе. Обратите внимание, что openid область присутствует по умолчанию.

Перенаправление URI, который мы создали в этом процессе, является конечной точкой в нашем сервисе: http://localhost:8081/login/oauth2/code/google.

Мы должны получить Идентификатор Клиента и Секрет Клиента от этого процесса.

3.1. Конфигурация Maven

Начнем с добавления этих зависимостей в наш файл пом проекта:


    org.springframework.boot
    spring-boot-starter-oauth2-client
    2.2.6.RELEASE

Стартовый артефакт агрегирует все зависимости, связанные с spring Security Client, включая:

  • весна-безопасность-oauth2-клиент зависимость от функциональности входа и клиента OAuth 2.0
  • библиотека JOSE для поддержки JWT

Как обычно, мы можем найти последнюю версию этого артефакта, используя Maven Центральной поисковой системы .

4. Базовая конфигурация с использованием пружинной загрузки

Во-первых, мы начнем с настройки нашего приложения, чтобы использовать регистрацию клиентов, которую мы только что создали с Google.

Использование Spring Boot делает это очень легко, так как все, что нам нужно сделать, это определить два свойства приложения:

spring:
  security:
    oauth2:
      client:
        registration: 
          google: 
            client-id: 
            client-secret: 

Давайте запустим наше приложение и попробуем получить доступ к конечной точке прямо сейчас. Мы увидим, что мы получаем перенаправлены на страницу Google Логин для нашего OAuth 2.0 Клиента.

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

Ранее, в нашем WebClient и OAuth 2 Поддержка должность , мы проанализировали внутренние о том, как весенняя безопасность обрабатывает OAuth 2.0 Авторизация серверов и клиентов.

Там мы увидели, что мы должны предоставить дополнительные данные, кроме Идентификатора Клиента и Секрет клиента, чтобы настроить КлиентРегистрация экземпляр успешно. Итак, как это работает?

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

Мы можем взглянуть на эти конфигурации в CommonOAuth2Провид enum.

Для Google перечисленный тип определяет свойства, такие как:

  • области по умолчанию, которые будут использоваться
  • конечная точка авторизации
  • конечная точка токена
  • конечная точка UserInfo, которая также является частью спецификации ядра OIDC

4.1. Доступ к информации о пользователях

Spring Security предлагает полезное представление пользователя Principal, зарегистрированного у поставщика OIDC, OidcUser сущность.

Помимо основных OAuth2ОфеникулированныйПринципиальный методы, эта сущность предлагает некоторые полезные функциональные возможности:

  • получить значение токена ID и требования, которые он содержит
  • получить претензии, предоставленные конечной точкой UserInfo
  • генерировать совокупность двух наборов

Мы можем легко получить доступ к этой сущности в контроллере:

@GetMapping("/oidc-principal")
public OidcUser getOidcUserPrincipal(
  @AuthenticationPrincipal OidcUser principal) {
    return principal;
}

Или с помощью SecurityContextHolder в фасоли:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getPrincipal() instanceof OidcUser) {
    OidcUser principal = ((OidcUser) authentication.getPrincipal());
    
    // ...
}

Если мы проверяем директора, мы увидим много полезной информации здесь, как имя пользователя, электронная почта, фотография профиля, и лока.

Кроме того, важно отметить, что Spring добавляет власти к основному на основе областей, которые он получил от поставщика, префиксированный с “ SCOPE_ “. Например, openid область становится SCOPE_openid предоставлены полномочия.

Эти органы могут быть использованы для ограничения доступа к определенным ресурсам, например :

@EnableWebSecurity
public class MappedAuthorities extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
          .authorizeRequests(authorizeRequests -> authorizeRequests
            .mvcMatchers("/my-endpoint")
              .hasAuthority("SCOPE_openid")
            .anyRequest().authenticated()
          );
    }
}

5. OIDC в действии

До сих пор мы узнали, как мы можем легко реализовать решение OIDC Login с помощью Spring Security

Мы видели преимущества, которые он несет, делегируя процесс идентификации пользователя поставщику OpenID, который, в свою очередь, поставляет подробную полезную информацию, даже в масштабируемой манере.

Но правда в том, что нам не нужно было иметь дело с каким-либо аспектом OIDC до сих пор. Это означает, что Весна делает большую часть работы за нас.

Таким образом, мы увидим, что происходит за кулисами, чтобы лучше понять, как эта спецификация введена в действие и быть в состоянии получить большую часть из него.

5.1. Процесс входа

Для того, чтобы увидеть это ясно, давайте в РестТемплет журналы, чтобы увидеть запросы, которые выполняет служба:

logging:
  level:
    org.springframework.web.client.RestTemplate: DEBUG

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

Во-первых, в зависимости от поставщика, который мы используем, и областей, которые мы настроили, мы можем видеть, что служба делает звонок в конечную точку UserInfo, о которой мы упоминали в начале.

А именно, если ответ авторизации получает по крайней мере один из профиль , электронной , адрес или телефонная область, структура будет называться конечной точкой UserInfo для получения дополнительной информации.

Даже если все будет означать, что Google должен получить профиль и электронной область – так как мы используем их в запросе на авторизацию – ОП получает свои пользовательские аналоги вместо этого, https://www.googleapis.com/auth/userinfo.email и https://www.googleapis.com/auth/userinfo.profile , Таким образом Весна не называет конечной точкой.

Это означает, что вся информация, которую мы получаем, является частью id Token.

Мы можем адаптироваться к этому поведению, создавая и предоставляя наши собственные OidcUserService пример:

@Configuration
public class OAuth2LoginSecurityConfig
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        Set googleScopes = new HashSet<>();
        googleScopes.add(
          "https://www.googleapis.com/auth/userinfo.email");
        googleScopes.add(
          "https://www.googleapis.com/auth/userinfo.profile");

        OidcUserService googleUserService = new OidcUserService();
        googleUserService.setAccessibleScopes(googleScopes);

        http
          .authorizeRequests(authorizeRequests -> authorizeRequests
            .anyRequest().authenticated())
          .oauth2Login(oauthLogin -> oauthLogin
            .userInfoEndpoint()
              .oidcUserService(googleUserService));
    }
}

Второе отличие мы будем наблюдать это вызов JWK Set URI. Как мы объяснили в нашем JWS и JWK пост , это используется для проверки JWT-форматированных ID Token подписи.

Далее мы детально проанализируем токен ID.

5.2. Токен ID

Естественно, спецификация OIDC охватывает и адаптируется к многим различным сценариям. В этом случае мы используем поток Кода Авторизации, и протокол указывает, что токен доступа и токен ID будут получены как часть ответа на конец токена.

Как мы уже говорили, OidcUser организация содержит претензии, содержащиеся в токене ID, и фактический токен в формате JWT, который можно проверить с помощью jwt.io .

Кроме того, Весна предлагает много удобных getters для получения стандартных претензий определяется спецификации в чистом виде.

Мы видим, id Token включает в себя некоторые обязательные претензии:

  • идентификатор эмитента, отформатированный как URL (например, ” https://accounts.google.com “)
  • идентификатор субъекта, который является ссылкой на конечного пользователя, содержащегося в эмитенте
  • время истечения срока действия токена
  • время выпуска токена
  • аудитории, которая будет содержать идентификатор клиента OAuth 2.0, который мы настроили

А также многие Стандартные претензии OIDC как те, которые мы упоминали ранее ( имя , местности , фотография , электронной ).

Поскольку они являются стандартными, мы можем ожидать, что многие поставщики получить по крайней мере некоторые из этих областей, и, следовательно, содействие разработке более простых решений.

5.3. Претензии и сферы применения

Как мы можем себе представить, претензии, которые извлекаются ОП, соответствуют тем объемам, которые мы (или Весенняя безопасность) настроены.

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

  • профиль , которые могут быть использованы для запроса претензий профиля по умолчанию (например, имя, preferred_username, картинка , и т.д )
  • электронной , чтобы получить доступ к электронной и email_verified Претензии
  • адрес
  • телефонная , на запросы phone_number и phone_number_verified Претензии

Несмотря на то, что Spring пока не поддерживает его, спецификация позволяет запрашивать отдельные претензии, указывая их в запросе авторизации.

6. Весенняя поддержка OIDC Discovery

Как мы объяснили во введении, OIDC включает в себя множество различных функций, помимо своей основной цели.

Возможности, которые мы собираемся проанализировать в этом разделе, и следующие не являются обязательными в OIDC. Поэтому важно понимать, что могут быть OPs, которые не поддерживают их.

Спецификация определяет механизм Обнаружения для RP, чтобы обнаружить ОП и получить информацию, необходимую для взаимодействия с ним.

В двух словах, OPs предоставить документ JSON стандартных метаданных. Информация должна обслуживаться хорошо известной конечной точкой местонахождения эмитента, /.well-known/openid-configuration .

Весна выигрывает от этого, позволяя нам настроить КлиентРегистрация только с одним простым свойством, расположение эмитента.

Но давайте перейти прямо в пример, чтобы увидеть это ясно.

Мы определим пользовательский КлиентРегистрация пример:

spring:
  security:
    oauth2:
      client:
        registration: 
          custom-google: 
            client-id: 
            client-secret: 
        provider:
          custom-google:
            issuer-uri: https://accounts.google.com

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

Мы можем даже просматривать эту конечную точку, чтобы взглянуть на информацию, предоставленную Google:

https://accounts.google.com/.well-known/openid-configuration

Мы можем видеть, например, конечные точки авторизации, токена и UserInfo, которые служба должна использовать, и поддерживаемые области.

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

7. Управление сессиями OpenID Connect

Эта спецификация дополняет функциональность Core, определяя:

  • различные способы мониторинга состояния входа конечного пользователя в ОП на постоянной основе, так что RP может выйти из конечного пользователя, который вышел из OpenID провайдера
  • возможность регистрации РП logout URIs в ОП в рамках регистрации Клиента, с тем чтобы быть уведомлены, когда конечный пользователь выходит из ОП
  • механизм уведомления ОП о том, что конечный пользователь вышел из сайта и, возможно, захочет выйти из ОП, а также

Естественно, не все OPs поддерживают все эти элементы, и некоторые из этих решений могут быть реализованы только в интерфейсе реализации через пользователя-агента.

В этом учебнике мы сосредоточимся на возможностях, предлагаемых Spring для последнего пункта списка, РП-инициированного Logout.

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

Если мы логотип (вызов /logout конечной точки), и мы делаем запрос на защищенный ресурс после этого, мы увидим, что мы можем получить ответ без необходимости войти в систему снова.

Тем не менее, это на самом деле не так; если мы проинспектим вкладку Network в консоли отладки браузера, мы увидим, что, когда мы попали в защищенную конечную точку во второй раз, когда мы получаем перенаправлены в OP Авторизация Конечной точки, и так как мы все еще вошли туда, поток завершается прозрачно, в конечном итоге в обеспеченной конечной точке почти мгновенно.

Конечно, это может быть не желаемое поведение в некоторых случаях. Давайте посмотрим, как мы можем реализовать этот механизм OIDC для решения этой проблемы.

7.1. Конфигурация поставщика OpenID

В этом случае мы будем настраивать и использовать экземпляр Okta в качестве нашего поставщика OpenID. Мы не будем подробно о том, как создать экземпляр, но мы можем следовать шагам это руководство , и имея в виду, что конечная точка обратного вызова Spring Security по умолчанию будет /login/oauth2/code/okta.

В нашем приложении мы можем определить регистрационные данные клиента с недвижимостью:

spring:
  security:
    oauth2:
      client:
        registration: 
          okta: 
            client-id: 
            client-secret: 
        provider:
          okta:
            issuer-uri: https://dev-123.okta.com

OIDC указывает, что конечная точка логотипа OP может быть указана в документе Discovery в качестве end_session_endpoint элемент.

7.2. Конфигурация LogoutSuccessHandler

Далее, мы должны настроить HttpSecurity логика логотипа, предоставляя индивидуальный ЛогоутСюкхендлер пример:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests(authorizeRequests -> authorizeRequests
        .mvcMatchers("/home").permitAll()
        .anyRequest().authenticated())
      .oauth2Login(oauthLogin -> oauthLogin.permitAll())
      .logout(logout -> logout
        .logoutSuccessHandler(oidcLogoutSuccessHandler()));
}

Теперь давайте посмотрим, как мы можем создать ЛогоутСюкхендлер для этой цели с помощью специального класса, предоставляемого Spring Security, OidcClientInitiatedLogoutSuccessHandler:

@Autowired
private ClientRegistrationRepository clientRegistrationRepository;

private LogoutSuccessHandler oidcLogoutSuccessHandler() {
    OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
      new OidcClientInitiatedLogoutSuccessHandler(
        this.clientRegistrationRepository);

    oidcLogoutSuccessHandler.setPostLogoutRedirectUri(
      URI.create("http://localhost:8081/home"));

    return oidcLogoutSuccessHandler;
}

Следовательно, нам нужно настроить этот URI в качестве действительного логотипа Перенаправить URI в панели конфигурации OP Client.

Очевидно, что конфигурация логотипа OP содержится в настройке регистрации клиента, так как все, что мы используем для настройки обработчика, КлиентРегистрацияРепозиторий фасоли присутствует в контексте.

Итак, что же произойдет сейчас?

После входа в наше приложение мы можем отправить запрос в /Логот конечная точка, предоставляемая Spring Security.

Если мы проверим журналы сети в консоли отладки браузера, Мы увидим, что мы получили перенаправлены на OP logout конечной точки, прежде чем, наконец, доступ к перенаправление URI мы настроены.

В следующий раз, когда мы получим доступ к конечной точке нашего приложения, которая требует проверки подлинности, нам придется снова войти в систему на нашей платформе OP, чтобы получить разрешения.

8. Заключение

Подводя итог, можно сказать, что в этом учебнике мы узнали много нового о решениях, предлагаемых OpenID Connect, и о том, как мы можем реализовать некоторые из них с помощью Spring Security.

Как всегда, все полные примеры можно найти в наш репо GitHub .