Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Рекомендации по аутентификации на основе токенов

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

Почему стоит использовать токены авторизации?

Многие люди считают, что если текущая стратегия работает хорошо (пусть и с некоторыми ошибками), то нет смысла что-то менять. Но токены авторизации могут принести множество выгод.

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

Что такое аутентификация на основе токенов?

Аутентификация на основе токенов – это один из многих методов веб-аутентификации, используемых для обеспечения безопасности процесса проверки. Существует аутентификация по паролю, по биометрии. Хотя каждый метод аутентификации уникален, все методы можно разделить на 3 категории:

  1. аутентификация по паролю (обычное запоминание комбинации символов)

  2. аутентификация по биометрии (отпечаток пальца, сканирование сетчатки глаза, FaceID)

  3. аутентификация токенов

Аутентификация токенов требует, чтобы пользователи получили сгенерированный компьютером код (или токен), прежде чем им будет предоставлен доступ в сеть. Аутентификация токенов обычно используется в сочетании с аутентификацией паролей для дополнительного уровня безопасности (двухфакторная аутентификация (2FA)).

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

Что такое json веб-токены?

JSON Web Token (JWT) – это открытый стандарт (RFC 7519), который определяет компактный и автономный способ безопасной передачи информации между сторонами в виде объекта JSON. Эта информация может быть подтверждена благодаря цифровой подписи. JWT может быть подписан с помощью секрета (с помощью алгоритма HMAC) или иным образом, например, по схемам RSA или ECDSA.

В своей компактной форме веб-токены JSON состоят из трех частей, разделенных точками: заголовок, полезная нагрузка, подпись. Поэтому JWT выглядит обычно выглядит следующим образом: «xxxx.yyyy.zzzz».

Заголовок состоит из двух частей: типа токена, которым является JWT, и используемого алгоритма подписи, такого как HMAC SHA256 или RSA.

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

Зарегистрированная – это набор ключей, который не является обязательными, но рекомендуются для обеспечения улучшения безопасности. Например, iss – уникальный идентификатор стороны, генерирующей токен, exp  – время в формате Unix Time, определяющее момент, когда токен станет не валидным, и другие.

Публичная информация может быть определена по желанию теми, кто использует JWT. Но они должны быть определены в реестре веб-токенов IANA JSON или определены как URI, который содержит устойчивое к коллизиям пространство имен. Частная – это пользовательская информация, созданная для обмена данными между сторонами, которые согласны их использовать. Получим вторую часть с помощью кодирования Base64Url.

Тоже не понял, что за прикол там происходит.

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

Безопасно ли использование токенов?

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

Аутентификация на основе токенов, когда она используется в тандеме с другими методами аутентификации, создает барьер 2FA, предназначенный для того, чтобы остановить даже самого продвинутого хакера. Поскольку токены могут быть получены только с устройства, которое их производит – будь то брелок или смартфон, системы авторизации токенов считаются очень безопасными и эффективными.

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

Как токены работают?

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

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

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

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

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

Реализация сервера авторизации oauth с помощью сервера авторизации spring

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Сервер авторизации в OAuth предназначен для выдачи маркера доступа, который позволяет клиентскому приложению использовать этот маркер доступа для запроса ресурса, который ему нужно получить. Сервер ресурсов будет подтверждать этот маркер доступа с помощью сервера авторизации каждый раз, когда клиентское приложение запрашивает ресурс, чтобы определить, следует ли разрешить клиентскому приложению доступ к этому ресурсу. Вы можете использовать множество различных открытых источников, таких как Keycloak, Spring Security OAuth (устаревший), или же новый проект Spring под названием Spring Authorization Server для реализации этого сервера авторизации. В этом руководстве я покажу вам, как использовать сервер авторизации Spring (Spring Authorization Server) для реализации сервера авторизации OAuth (OAuth Authorization Server)!

Сначала я создам новый проект Spring Boot с Web Starter, Security Starter:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

и сервер авторизации Spring:

<dependency>
    <groupId>org.springframework.security.experimental</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>0.1.2</version>
</dependency>

для примера.

Результат:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Конфигурация сервера авторизации

Сначала я создам новый класс AuthorizationServerConfiguration для настройки сервера авторизации.

По умолчанию сервер авторизации Spring  поддерживает класс OAuth2AuthorizationServerConfiguration с конфигурациями по умолчанию для сервера авторизации. Если взглянуть на код этого класса, то можно увидеть, что он определяет метод applyDefaultSecurity(), который инициализирует объект OAuth2AuthorizationServerConfigurer с целью применения конфигураций по умолчанию, которые определяет этот класс OAuth2AuthorizationServerConfigurer:

public static void applyDefaultSecurity(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
            new OAuth2AuthorizationServerConfigurer<>();
    RequestMatcher endpointsMatcher = authorizationServerConfigurer
            .getEndpointsMatcher();

    http
        .requestMatcher(endpointsMatcher)
        .authorizeRequests(authorizeRequests ->
            authorizeRequests.anyRequest().authenticated()
        )
        .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
        .apply(authorizationServerConfigurer);
}

Как вы можете видеть, метод applyDefaultSecurity() также  определяет безопасность для конечных точек сервера авторизации по умолчанию.

Класс OAuth2AuthorizationServerConfiguration также определяет бин (bean) для класса SecurityFilterChain, который вызывает метод applyDefaultSecurity() для регистрации этих конфигураций по умолчанию в Spring Security сервера авторизации.

Вы можете импортировать этот класс OAuth2AuthorizationServerConfiguration с помощью аннотации Spring @Import, чтобы использовать эти конфигурации по умолчанию:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfiguration {

}

или если вы хотите добавить что-то из пользовательского кода, то давайте объявим бин для класса SecurityFilterChain и вызовем метод applyDefaultSecurity() следующим образом:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class AuthorizationServerConfiguration {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

        return http.formLogin(Customizer.withDefaults()).build();
    }

}

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

При использовании сервера авторизации нам важно определить веб-ключ JSON для проверки информации в маркере доступа, который пользователь запросил у сервера ресурсов, выданном сервером авторизации. Для завершения настройки этого сервера авторизации требуется бин JwtDecoder с объектом класса JWKSource. Мы можем определить бины для этих объектов следующим образом:

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
    return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
    RSAKey rsaKey = generateRsa();
    JWKSet jwkSet = new JWKSet(rsaKey);

    return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

private static RSAKey generateRsa() throws NoSuchAlgorithmException {
    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() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);

    return keyPairGenerator.generateKeyPair();
}

Конфигурация Spring Security

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

Мы можем определить SecurityFilterChain следующим образом:

package com.huongdanjava.springauthorizationserver;

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SpringSecurityConfiguration {

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

        return http.build();
    }

}

На этом этапе страница входа будет отображаться, если пользователь не вошел в систему.

Регистрация клиента на сервере авторизации

Сервер авторизации Spring использует класс RegisteredClient для объявления информации клиента, зарегистрированного на сервере авторизации, и реализует интерфейс RegisteredClientRepository для хранения информации всех этих клиентов.

Мы можем объявить информацию о клиенте, используя память или соответствующую базу данных:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Для простоты я буду использовать память следующим образом:

@Bean
public RegisteredClientRepository registeredClientRepository() {
    RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId("huongdanjava")
        .clientSecret("{noop}123456")
        .clientAuthenticationMethod(ClientAuthenticationMethod.POST)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .redirectUri("https://oidcdebugger.com/debug")
        .scope(OidcScopes.OPENID)
        .build();

    return new InMemoryRegisteredClientRepository(registeredClient);
}

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

Что такое идентификатор клиента, объяснять не нужно! По поводу типа гранта авторизации, следует отметить, что сервер авторизации Spring поддерживает все типы грантов OAuth 2.

Секрет клиента зависит от типа клиента, который мы хотим определить, если наш клиент конфиденциальный, см. также Типы клиентов в OAuth 2.0, то секрет клиента обязателен. Здесь вам нужно объявить, как зашифровать клиентский секрет с помощью PasswordEncoder, если вы не хотите шифровать его в целях тестирования, то можно использовать NoOpPasswordEncoder, объявив “{noop}” в начале клиентского секрета, как я сделал выше. Помните, что это применяется только в тестовых целях!

Если клиент является конфиденциальным, нам также понадобится Метод Client Authentication для определения маркера доступа.

В зависимости от типа гранта клиента, который вы определяете, есть и другая необходимая информация, которую нам нужно объявить. Например, в моем случае я определяю клиента с типом разрешения authorization_code, поэтому мне нужно определить redirect_uri. Здесь я буду использовать инструмент https://oidcdebugger.com/ для получения кода авторизации, поэтому я определяю redirect_uri со значением https://oidcdebugger.com/debug, как это можно видеть в примере.

В зависимости от ваших потребностей, давайте определим информацию о клиенте соответствующим образом.

Регистрация пользователя на сервере авторизации

Для регистрации пользователя на сервере авторизации используется память со следующим объявлением:

@Bean
public UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
        .username("admin")
        .password("password")
        .roles("ADMIN")
        .build();

    return new InMemoryUserDetailsManager(user);
}

Итак, на данном этапе мы завершили базовую конфигурацию для Authorization Server.

Чтобы проверить результаты, я воспользуюсь инструментом https://oidcdebugger.com/, как упоминалось выше, со следующим объявлением:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Нажмите кнопку Send request (Отправить запрос) на этой странице, после чего вы откроете страницу входа на сервер авторизации, которая будет выглядеть следующим образом:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Войдите в систему с информацией, которую мы объявили выше, и увидите следующие результаты:

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

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

Реализация сервера авторизации OAuth с помощью сервера авторизации Spring / Хабр

Материал подготовлен в рамках курса «Разработчик на Spring Framework». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Типы токенов авторизации

Токены авторизации различаются по типам. Рассмотрим их:

  1. Устройства, которые необходимо подключить физически. Например: ключи, диски и тому подобные. Тот, кто когда-либо использовал USB-устройство или смарт-карту для входа в систему, сталкивался с подключенным токеном.

  2. Устройства, которые находятся достаточно близко к серверу, чтобы установить с ним соединение, но оно не подключаются физически. Примером такого типа токенов может служить “magic ring” от компании Microsoft.

  3. устройства, которые могут взаимодействовать с сервером на больших расстояниях.

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

Похожее:  Вход учителя в виртуальный кабинет Teams, ГБОУ Школа № 554, Москва

Добавить комментарий

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