У меня есть проект 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);
}
}




Если вы измените цепочку авторизаций на
...
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))