Проблема с авторизацией в конфигурации безопасности Spring API Gateway

1
8

я настроил свой API Gateway для проверки, разрешен ли пользователю доступ к некоторым маршрутам, вот конфигурация, но я получаю Access denied, хотя у пользователя есть нужные роли вот пример того, что я получаю:

2024-08-20T13:08:38.091+01:00 DEBUG 27199 --- [api-gateway] [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter : [f2b2d9a7-4] HTTP POST "/api/course" токен: eyJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJST0xFX1VTRVIiLCJST0xFX0FETUlOIl0sInN1YiI6ImFkbWluLmF5YUBleGFtcGxlLmNvbSIsImlhdCI6MTcyNDE1NTUwOSwiZXhwIjoxNzI0MTU2OTQ5fQ.1WVtd-2pI--W8mKxPWBBjLApEUo0YLl4xoz2AjYUNFY Утверждения: {[email protected], exp=2024-08-20T12:29:09Z, iat=2024-08-20T12:05:09Z, roles=[ROLE_USER, ROLE_ADMIN]} Роль из токена: ROLE_USER Роль из токена: ROLE_ADMIN

пользователь имеет роль ADMIN, но затем, когда я пытаюсь создать курс, я получаю отказ в доступе: 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.w.s.s.DefaultWebSessionManager: Создан новый WebSession. 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: Попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher: Запрос 'POST /api/course' не соответствует 'POST /logout' 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: совпадений не найдено 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/api/auth/', method=null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher: запрос 'POST /api/course' не соответствует 'null /api/auth/' 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: совпадений не найдено 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/eureka/', method=null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher: Запрос "POST /api/course" не соответствует "null /eureka/" 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: Совпадений не найдено 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/api/user/', method=null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher : Запрос 'POST /api/course' не соответствует 'null /api/user/' 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Нет найдено совпадений 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: Попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/POST', method=null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher: Запрос 'POST /api/course' не соответствует 'null /POST' 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: Попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/api/course', method=null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] athPatternParserServerWebExchangeMatcher: Проверка соответствия запроса: '/api/course'; против '/api/course' 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: сопоставлено 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] a.DelegatingReactiveAuthorizationManager: проверка авторизации на '/api/course' с использованием org.springframework.security.authorization.AuthorityReactiveAuthorizationManager@31960a5b 2024-08-20T13:08:38.095+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.s.w.s.a.AuthorizationWebFilter : Авторизация не удалась: Доступ запрещен 2024-08-20T13:08:38.095+01:00 DEBUG 27199 --- [api-gateway] [ parallel-4] o.s.w.s.adapter.HttpWebHandlerAdapter : [f2b2d9a7-4] Завершено 200 OK

package org.example.config;

import io.jsonwebtoken.JwtException;
import jakarta.ws.rs.HttpMethod;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.List;


@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuerUri;
    private static final String SECRET_KEY = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437";


    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity serverHttpSecurity) {
        serverHttpSecurity
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchange -> {
                    exchange
                            .pathMatchers("/api/auth/**").permitAll()
                            .pathMatchers("/eureka/**").permitAll()

                            .pathMatchers("/api/user/**").hasRole("MASTER")

                            .pathMatchers(HttpMethod.POST, "/api/course").hasAnyRole("MASTER", "ADMIN")
                            .pathMatchers(HttpMethod.PUT, "/api/course/**").hasAnyRole("MASTER", "ADMIN")
                            .pathMatchers(HttpMethod.DELETE, "/api/course/**").hasRole("MASTER")
                            .pathMatchers(HttpMethod.GET, "/api/course/**").hasAnyRole("MASTER", "ADMIN", "USER")

                            .anyExchange().authenticated();
                })
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtDecoder(jwtDecoder())))
                .exceptionHandling((exception)->exception.accessDeniedHandler(((exchange, denied) -> {
                    // Log the denied access details
                    ReactiveSecurityContextHolder.getContext()
                            .doOnNext(securityContext -> {
                                Authentication authentication = securityContext.getAuthentication();
                                if (authentication != null) {
                                    System.out.println("Authenticated user: " + authentication.getName());
                                    System.out.println("Authorities: " + authentication.getAuthorities());
                                } else {
                                    System.out.println("No authentication found in context");
                                }
                            })
                            .subscribe();
                    System.out.println("Access Denied for request path: " + exchange.getRequest().getPath());
                    return exchange.getResponse().setComplete();

                })));

        return serverHttpSecurity.build();
    }

    @Bean
    public ReactiveJwtDecoder jwtDecoder() {

        // Decode the Base64-encoded SECRET_KEY
        byte[] keyBytes = Base64.getDecoder().decode(SECRET_KEY);
        SecretKey secretKey = new SecretKeySpec(keyBytes, "HmacSHA256");

        // Use the secret key to create a NimbusReactiveJwtDecoder
        NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withSecretKey(secretKey).build();

        return token -> {
            System.out.println("token: " + token);
            return jwtDecoder.decode(token)
                    .doOnNext(jwt -> {
                        System.out.println("Claims: " + jwt.getClaims());
                        List<String> roles = jwt.getClaimAsStringList("roles");
                        if (roles != null) {
                            roles.forEach(role -> System.out.println("Role from token: " + role));
                        }
                    });
        };
    }

}

Савелий
Вопрос задан22 марта 2024 г.

1 Ответ

Ваш ответ

Загрузить файл.