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

Сервер авторизации Spring Security OAuth

Узнайте, как использовать сервер авторизации Spring Security OAuth.

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

1. введение

OAuth – это открытый стандарт, описывающий процесс авторизации. Он может использоваться для авторизации доступа пользователей к API. Например, API REST может ограничить доступ только для зарегистрированных пользователей с соответствующей ролью.

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

В этом уроке мы реализуем простой сервер OAuth с помощью экспериментального модуля Spring Security OAuth Authorization Server .

В процессе мы создадим клиент-серверное приложение, которое будет извлекать список статей Baeldung из REST API. Как для клиентских, так и для серверных служб потребуется проверка подлинности OAuth.

2. Реализация Сервера Авторизации

Давайте начнем с рассмотрения конфигурации сервера авторизации OAuth. Он будет служить источником аутентификации как для ресурса статьи, так и для клиентских серверов.

2.1. Зависимости

Во-первых, нам нужно будет добавить несколько зависимостей к вашему pom.xml файл:


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


    org.springframework.boot
    spring-boot-starter-security
    2.4.3


    org.springframework.security.experimental
    spring-security-oauth2-authorization-server
    0.1.0

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

Теперь давайте настроим порт, на котором будет работать наш сервер аутентификации, установив свойство server.port в файле application.yml :

server:
  port: 9000

После этого мы можем перейти к конфигурации Spring beans. Во-первых, нам понадобится класс @Configuration и импорт конфигурации сервера авторизации OAuth . Внутри класса конфигурации мы создадим несколько компонентов, специфичных для OAuth. Первым из них будет репозиторий клиентских сервисов. В нашем примере у нас будет один клиент, созданный с помощью класса Registered Client builder:

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
          .clientId("articles-client")
          .clientSecret("secret")
          .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
          .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
          .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
          .redirectUri("http://localhost:8080/login/oauth2/code/articles-client-oidc")
          .redirectUri("http://localhost:8080/authorized")
          .scope(OidcScopes.OPENID)
          .scope("articles.read")
          .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }
}

Свойства, которые мы настраиваем, следующие:

  • Идентификатор клиента – Spring будет использовать его для определения того, какой клиент пытается получить доступ к ресурсу
  • Секретный код клиента – секрет, известный клиенту и серверу, который обеспечивает доверие между ними
  • Метод аутентификации – в нашем случае мы будем использовать базовую аутентификацию, которая представляет собой просто имя пользователя и пароль
  • Тип предоставления авторизации – мы хотим, чтобы клиент мог генерировать как код авторизации, так и токен обновления
  • URI перенаправления – клиент будет использовать его в потоке на основе перенаправления
  • Область действия – этот параметр определяет разрешения, которые могут быть у клиента. В нашем случае мы получим необходимые OidcScopes.OPENID и наши пользовательские статьи. читать

Каждый сервер авторизации нуждается в своем ключе подписи для токенов, чтобы поддерживать надлежащую границу между доменами безопасности. Давайте сгенерируем 2048-байтовый ключ RSA:

@Bean
public JWKSource jwkSource() {
    RSAKey rsaKey = generateRsa();
    JWKSet jwkSet = new JWKSet(rsaKey);
    return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() {
    KeyPair keyPair = generateRsaKey();
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
    return new RSAKey.Builder(publicKey)
      .privateKey(privateKey)
      .keyID(UUID.randomUUID().toString())
      .build();
}

private static KeyPair generateRsaKey() {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    return keyPairGenerator.generateKeyPair();
}

За исключением ключа подписи, каждый сервер авторизации также должен иметь уникальный URL-адрес эмитента. Мы установим его как http://127.0.0.1 на порту 9000 создав Настройки поставщика bean:

@Bean
public ProviderSettings providerSettings() {
    return new ProviderSettings().issuer("http://127.0.0.1:9000");
}

Наконец, мы включим модуль Spring web security с помощью @EnableWebSecurity аннотированного класса конфигурации:

@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests(authorizeRequests ->
          authorizeRequests.anyRequest().authenticated()
        )
          .formLogin(withDefaults());
        return http.build();
    }

    // ...
}

Здесь мы вызываем authorizeRequests.anyRequest().authenticated () , чтобы потребовать аутентификации для всех запросов, и мы предоставляем аутентификацию на основе формы, вызывая метод formLogin(defaults ()) .

Кроме того, мы определим набор примеров пользователей, которые мы будем использовать для тестирования. Для этого примера давайте создадим репозиторий только с одним пользователем-администратором:

@Bean
UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
      .username("admin")
      .password("password")
      .build();
    return new InMemoryUserDetailsManager(user);
}

3. Сервер ресурсов

Теперь мы создадим сервер ресурсов, который будет возвращать список статей из точки доступа. Конечные точки должны разрешать только запросы, аутентифицированные на нашем сервере OAuth.

3.1. Зависимости

Во-первых, давайте включим необходимые зависимости:


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


    org.springframework.boot
    spring-boot-starter-security
    2.4.3


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

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

Прежде чем мы начнем с кода реализации, мы должны настроить некоторые свойства в файле application.yml . Первый – это порт сервера:

server:
  port: 8090

Далее, пришло время для настройки безопасности. Нам нужно настроить правильный URL-адрес для нашего сервера аутентификации с хостом и портом, который мы настроили в ProviderSettings bean ранее:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://127.0.0.1:9000

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

@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.mvcMatcher("/articles/**")
          .authorizeRequests()
          .mvcMatchers("/articles/**").access("hasAuthority('SCOPE_articles.read')")
          .and()
          .oauth2ResourceServer()
          .jwt();
        return http.build();
    }
}

Как показано здесь, мы также вызываем метод oauth2 ResourceServer () , который настроит соединение с сервером OAuth на основе конфигурации application.yml .

3.3. Контроллер изделий

Наконец, мы создадим контроллер REST, который будет возвращать список статей в конечной точке GET/articles :

@RestController
public class ArticlesController {

    @GetMapping("/articles")
    public String[] getArticles() {
        return new String[] { "Article 1", "Article 2", "Article 3" };
    }
}

4. Клиент API

В последней части мы создадим клиент REST API, который будет получать список статей с сервера ресурсов.

4.1. Зависимости

Для начала давайте включим необходимые зависимости:


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


    org.springframework.boot
    spring-boot-starter-security
    2.4.3


    org.springframework.boot
    spring-boot-starter-oauth2-client
    2.4.3


    org.springframework
    spring-webflux
    5.3.4


    io.projectreactor.netty
    reactor-netty
    1.0.4

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

Как и ранее, мы определим некоторые свойства конфигурации для целей аутентификации:

server:
  port: 8080

spring:
  security:
    oauth2:
      client:
        registration:
          articles-client-oidc:
            provider: spring
            client-id: articles-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: openid
            client-name: articles-client-oidc
          articles-client-authorization-code:
            provider: spring
            client-id: articles-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized"
            scope: articles.read
            client-name: articles-client-authorization-code
        provider:
          spring:
            issuer-uri: http://127.0.0.1:9000

Теперь давайте создадим экземпляр WebClient для выполнения HTTP-запросов к нашему серверу ресурсов. Мы будем использовать стандартную реализацию с одним добавлением фильтра авторизации OAuth:

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
      new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
    return WebClient.builder()
      .apply(oauth2Client.oauth2Configuration())
      .build();
}

Веб-клиент требует Авторизованный клиентский менеджер OAuth2 как зависимость. Давайте создадим реализацию по умолчанию:

@Bean
OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) {

    OAuth2AuthorizedClientProvider authorizedClientProvider =
      OAuth2AuthorizedClientProviderBuilder.builder()
        .authorizationCode()
        .refreshToken()
        .build();
    DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
      clientRegistrationRepository, authorizedClientRepository);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}

Наконец, мы настроим веб-безопасность:

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
          .authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
          )
          .oauth2Login(oauth2Login ->
            oauth2Login.loginPage("/oauth2/authorization/articles-client-oidc"))
          .oauth2Client(withDefaults());
        return http.build();
    }
}

Здесь, как и на других серверах, нам потребуется аутентификация каждого запроса. Кроме того, нам необходимо настроить URL-адрес страницы входа в систему (определенный в .yml config) и клиент OAuth.

4.3. Статьи Клиентский контроллер

Наконец, мы можем создать контроллер доступа к данным. Мы будем использовать ранее настроенный Веб-клиент для отправки HTTP-запроса на наш сервер ресурсов:

@RestController
public class ArticlesController {

    private WebClient webClient;

    @GetMapping(value = "/articles")
    public String[] getArticles(
      @RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient
    ) {
        return this.webClient
          .get()
          .uri("http://localhost:8090/articles")
          .attributes(oauth2AuthorizedClient(authorizedClient))
          .retrieve()
          .bodyToMono(String[].class)
          .block();
    }
}

В приведенном выше примере мы берем маркер авторизации OAuth из запроса в виде Авторизованный клиент OAuth2 класс. Он автоматически привязывается к Spring с помощью аннотации @RegisterdOAuth2AuthorizedClient с надлежащей идентификацией. В нашем случае он извлекается из article-client-authorization-code , который мы настроили ранее в файле .yml .

Этот маркер авторизации далее передается в HTTP-запрос.

4.4. Доступ к Списку статей

Теперь, когда мы заходим в браузер и пытаемся получить доступ к http://localhost:8080/articles страница, будет автоматически перенаправлена на страницу входа в систему сервера OAuth в разделе http://127.0.0.1:9000/login URL:

После предоставления правильного имени пользователя и пароля сервер авторизации перенаправит нас обратно на запрошенный URL – адрес-список статей.

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

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

В этой статье мы узнали, как настроить, настроить и использовать сервер авторизации Spring Security OAuth.

Как всегда, весь исходный код доступен на GitHub .