Запрос GraphQL всегда имеет код 403 даже при наличии действующего токена JWT

У меня есть проект Spring Boot с рабочими конечными точками REST, защищенными фильтром JWT. Например, запрос GET потребует маркера носителя, а затем вернет данные пользователя. Все конечные точки REST работают как положено. Теперь мы хотели реализовать запросы GraphQL для будущей выборки данных.

Вот здесь и возникает проблема. Если конечная точка /graphql не защищена в конфигурации безопасности

.authorizeHttpRequests(
            authorizeRequests ->
                authorizeRequests
                    .requestMatchers("/graphql/**")
                    .permitAll() // Whitelist
                    .anyRequest().authenticated() // Everything else should be authenticated
        )

запрос проходит, и я получаю нужные данные. Но если я теперь вынесу конечную точку GraphQL из этого «белого списка», каждый запрос, с действительным токеном доступа или без него, вернется как 403. Также нет сообщения об ошибке в Postman или консоли IntelliJ, где я мог бы отследить, где этот код взят из.

Для получения дополнительных ресурсов это моя полная конфигурация безопасности:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(
                        authorizeRequests ->
                                authorizeRequests
                                        .requestMatchers("/graphql/**")
                                        .permitAll()
                                        .anyRequest().authenticated()
                )
                .sessionManagement(
                        sessionManagement ->
                                sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}

и это мой фильтр JWT:

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

  private final JwtService jwtService;
  private final UserDetailsService userDetailsService;

  @Override
  protected void doFilterInternal(
      @NotNull HttpServletRequest request,
      @NotNull HttpServletResponse response,
      @NotNull FilterChain filterChain)
      throws ServletException, IOException {
    final String authHeader = request.getHeader("Authorization");
    final String jwt;
    final String userEmail;

    if (authHeader == null || !authHeader.startsWith("Bearer ")) {
      filterChain.doFilter(request, response);
      return;
    }

    jwt = authHeader.substring(7);// beginIndex is 7 bc "Bearer " is 7
    userEmail = jwtService.extractUserEmail(jwt);

    if (userEmail != null
        && SecurityContextHolder.getContext().getAuthentication() == null) { // check f if user is already authenticated
      UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);

      if (jwtService.isTokenValid(jwt, userDetails)) {
        UsernamePasswordAuthenticationToken authToken =
            new UsernamePasswordAuthenticationToken(
                userDetails, null, userDetails.getAuthorities());

        authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authToken);
      }
    }

    filterChain.doFilter(request, response);
  }
}
Пользовательский скаляр 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 .
2
0
113
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы измените цепочку авторизаций на

...
authorizeRequests
  .anyRequest()
  .permitAll()
...

Он должен пропускать все запросы, и тогда вы сможете обрабатывать свои авторизации с помощью Spring Security.

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

Похоже, это проблема, вызванная Spring Security 6 (Подробнее: https://github.com/spring-projects/spring-graphql/issues/594)

Вы можете изменить свой код следующим образом:

    @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    return httpSecurity
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(
                    authorizeRequests ->
                            authorizeRequests
                                    .requestMatchers("/graphql/**")
                                    .permitAll()
                                    .anyRequest().authenticated()
            )
            .sessionManagement(
                    sessionManagement ->
                            sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authenticationProvider(authenticationProvider)
            .addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class)
            .securityContext((securityContext) -> securityContext.requireExplicitSave(false))
            .build();
}

Осталось добавить: .securityContext((securityContext) -> securityContext.requireExplicitSave(false))

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

Ошибка с CORS в проекте с Angular и Laravel
Ошибка: несоответствие URI перенаправления при интеграции аутентификации Microsoft с Django
Ошибка входа в Azure AD B2C с ошибкой поставщика удостоверений Microsoft: для погашения кода авторизации между источниками требуется ключ подтверждения для обмена кодами
Избегайте сброса AccessFailedCount с помощью CheckPasswordSignIn в ASP.Net Identity
«Размер файла cookie сеанса превышает разрешенные 4096 байт». Получение этой ошибки следующей аутентификации после обновления nextJs 14.1.4 с 14.1.0
Разрешить не работать в API с идентификатором ASP.NET Core и токеном JWT
Не удалось выполнить аутентификацию, поскольку учетная запись пользователя заблокирована, ошибка аутентификации безопасности Springboot
Правильный способ обработки входа в систему и токенов в приложении Android, когда выходить из системы?
Аутентификация Blazor с использованием AzureAD — для учетных записей в любом каталоге организации
Google Workspace + AWS Cognito | Единый выход с использованием интеграции SAML