Spring Security oauth2Login против oauth2Client

Я хочу, чтобы мое приложение могло отправлять запросы REST API от имени пользователей определенной платформы. Я зарегистрировал свое приложение на платформе, и у них есть поддержка OAuth2 со следующими конечными точками:

  • GET /login/oauth2/auth, который является URI авторизации для запуска потока аутентификации_кода.
  • POST /login/oauth2/token, куда я отправляю код (при условии, что пользователь дал согласие) и получаю ответ, содержащий токен доступа и токен обновления:
{
  "access_token": "...",
  "refresh_token": "...",
  "user": {"id":5, "name": "First Last"}
}

Сначала я использовал Spring Security oauth2Login:

spring:
  security:
    oauth2:
      client:
        provider:
          the-provider:
            authorization-uri: https://provider.domain.com/login/oauth2/auth
            token-uri: https://provider.domain.com/login/oauth2/token
            user-info-authentication-method: Bearer
            user-info-uri: https://provider.domain.com/api/users/me
        registration:
          the-provider:
            authorization-grant-type: authorization_code
            client-id: my-client-id
            client-secret: myClientS3cret
            redirect-uri: "{baseUrl}/login/oauth2/code/the-provider"
@Bean
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
    http.oauth2Login(Customizer.withDefaults());
    http.authorizeHttpRequests(r -> r.anyRequest().authenticated());
    return http.build();
}

По умолчанию пользователь, который обращается к моему приложению по адресу "/", автоматически проходит через поток OAuth2, а Spring Security помещает информацию о пользователе в контекст безопасности. Это хорошо, но я не понимаю, как получить/использовать токен доступа для последующих запросов API от имени пользователя.

Authentication — это OAuth2AuthenticationToken, principal которого DefaultOAuth2User — это authorities, чьи [ "OAUTH2_USER" ] являются attributes и чьи user-info-uri являются данными из oauth2Client, но я не вижу нигде, где бы содержался токен доступа и токен обновления.

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

Просто чтобы немного прояснить то, что я себе представляю, это будет что-то вроде

@Controller
public class MyController {
    
    private final ReportService reportService;
    
    public MyController(final ReportService reportService) {
        this.reportService = reportService;
    }
    
    @GetMapping({"", "/"})
    public String index(final Model model) {
        model.addAttribute("report", reportService.generateReport());
        return "index";
    }
}

где access_token делает пару запросов REST API, используя oauth2Login, сгенерированный для пользователя, который, как я полагаю, будет отключен от аутентификации контекста безопасности.

Думаю, мой вопрос как бы направлен на то, могу ли я получить это из коробки с помощью Spring Security (через oauth2Client или GET /login/oauth2/auth), или мне нужно будет создать что-то собственное с помощью примитивов Spring Security?

Я добавил образец с «моей» закваской, чтобы дать вам представление о HttpServiceProxyFactory волшебстве весны. Если вы можете получить спецификацию OpenAPI для своей удаленной службы, то openapi-generator-maven-plugin сможете генерировать @HttpExchange интерфейсы (с DTO). В этой ситуации клиент REST для этой службы создается Spring и авторизуется с использованием OAuth2 «моим» стартером.

ch4mp 08.08.2024 21:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
0
1
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

oauth2Login настраивает приложение с кодом авторизации и обновляет потоки токенов. Итак, по задумке приложение с oauth2Login является клиентом OAuth2.

oauth2Client — это приложение Spring, которое получает токены с сервера авторизации. При использовании Boot клиенты OAuth2 настраиваются со свойствами spring.security.oauth2.client.*. Чтобы oauth2Login работал, эти свойства должны иметь хотя бы одну регистрацию в authorization_code.

Обратите внимание, что вы можете сделать любое приложение (не только oauth2Login, но также oauth2ResourceServer, formLogin и т. д.) клиентом OAuth2, если ему необходимо авторизовать запросы к серверу ресурсов.

В клиентском приложении Spring OAuth2 вы можете получить токены из (Reactive)OAuth2AuthorizedClientManager.

Сделай это сам

@Service
public class ReportServiceImpl implements ReportService {
    private final OAuth2AuthorizedClientManager clients;
    private final OAuth2AuthorizeRequest req;
    

    public ReportServiceImpl(OAuth2AuthorizedClientManager clients, @Value("${report-client-registration-id:the-provider}") String reportClientRegistrationId) {
        super();
        this.clients = clients;
        this.req = OAuth2AuthorizeRequest.withClientRegistrationId(reportClientRegistrationId).build();
    }


    @Override
    public ReportDto generateReport() {
        final var authorized = clients.authorize(req);
        final var bearerString = "Bearer %s".formatted(authorized.getAccessToken().getTokenValue());
        // TODO: set the bearerString as Authorization header to the request using your favorite client;
        
        return new ReportDto();
    }

}

Обратите внимание, что большинство клиентов REST включают функции прозрачной авторизации всех запросов (получите токен от «авторизованного клиента» и установите заголовок авторизации, не загромождая код службы). Например, RestClient можно настроить с помощью requestInterceptor функций, а WebClient с помощью filter функций.

Используя spring-addons-starter-rest

Чтобы не писать код REST-клиента и его авторизацию, публикую стартер Spring Boot. Он интегрируется с Spring HttpServiceProxyFactory, чтобы добавить RestClient (или WebClient) автоматическую настройку HTTP-прокси и авторизацию OAuth2 (или Basic).

Пример описания удаленной службы (вероятно, созданный на основе спецификации OpenAPI с использованием такого инструмента, как openapi-generator-maven-plugin):

@HttpExchange(accept = MediaType.APPLICATION_JSON_VALUE)
public interface ReportApi {
    @GetExchange(url = "/report")
    ReportDto generateReport();
}

spring-addons зависимость:

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-starter-rest</artifactId>
    <version>7.8.8</version>
</dependency>

spring-addons конфигурация для использования токена от авторизованного клиента (альтернативной стратегией является пересылка носителя, который авторизовал входящий запрос, но это возможно только на ресурсном сервере):

com:
  c4-soft:
    springaddons:
      rest:
        client:
          report-api:
            base-url: http://reporting-service
            authorization:
              oauth2:
                # Kept your registration ID, even if it is rather confusing
                oauth2-registration-id: the-provider

Создав клиент удаленного обслуживания:

@Configuration
public class RestConfiguration {
    @Bean
    ReportApi reportApi(SpringAddonsRestClientSupport restSupport) {
        return restSupport.service("report-api", ReportApi.class);
    }
}

Использование в компоненте Spring:

@Controller
@RequiredArgsConstructor
public class SomeController {
    // Abracadabra! This is successfully auto-wired
    private final ReportApi reportApi;

    ...
    
}

Обратите внимание, что кода для реализации или авторизации ReportApi совершенно нет, все генерируется!

В моих проектах даже клиентские интерфейсы (те, которые украшены @HttpExchange, как ReportApi выше) генерируются из спецификации OpenAPI, которая сама генерируется Swagger из источников удаленного сервиса @RestController (украшенных вариантами @RequestMapping).

Другие вопросы по теме