Я настроил свой шлюз API, чтобы проверить, разрешен ли пользователю доступ к некоторым маршрутам, вот конфигурация, но я получаю отказ в доступе, хотя у пользователя есть правильные роли вот пример того, что я получаю:
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.eyJyb2xlcyI6WyJST0xFX1VTRVIiLCJST0xFX0FETUlOIl0sInN1YiI6ImFkbWluLmF5YUBleGFtcGxlLmNvbSIsImlhdCI6MTcyNDE1NTUwOSwiZXhwIjoxNzI0M TU2OTQ5fQ.1WVtd-2pi--W8mKxPWBBjLApEUo0YLl4xoz2AjYUNFY Утверждения: {[email защищен], 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] [параллельный-4] o.s.w.s.s.DefaultWebSessionManager: создан новый веб-сеанс. 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-шлюз] [параллельный-4] athPatternParserServerWebExchangeMatcher: запрос "POST/api/course" не соответствует "POST/logout" 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [параллельный-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/', метод = ноль} 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] [параллельный-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/', метод = нулевой} 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] [параллельный-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: совпадений не найдено 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-gateway] [параллельный-4] o.s.s.w.s.u.m.OrServerWebExchangeMatcher: попытка сопоставления с использованием PathMatcherServerWebExchangeMatcher{pattern='/api/user/', метод = ноль} 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] [параллельный-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', метод = null} 2024-08-20T13:08:38.094+01:00 DEBUG 27199 --- [api-шлюз] [параллельный-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] [параллельный-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] [параллельный-4] o.s.s.w.s.a.AuthorizationWebFilter: Ошибка авторизации: доступ запрещен 2024-08-20T13:08:38.095+01:00 DEBUG 27199 --- [api-gateway] [параллельный-4] o.s.w.s.adapter.HttpWebHandlerAdapter: [f2b2d9a7-4] Завершено 200 ОК
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));
}
});
};
}
}