Код: Выделить всё
pom.xmlКод: Выделить всё
org.springframework.boot
spring-boot-starter-oauth2-resource-server
org.springframework.security
spring-security-oauth2-jose
Код: Выделить всё
application.yamlКод: Выделить всё
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${JWT_ISSUER_URI:http://notavailable.io/realms/my-realm}
Код: Выделить всё
SecurityConfig.javaКод: Выделить всё
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange(exchanges -> exchanges.pathMatchers("/actuator/**")
.permitAll()
.anyExchange()
.hasAuthority("SCOPE_foobar"))
.oauth2ResourceServer(oAuth2ResourceServerSpec -> oAuth2ResourceServerSpec.jwt(withDefaults())
.authenticationEntryPoint((webExchance, exception) -> handleAuthError(webExchance))
.accessDeniedHandler((webExchance, exception) -> handleAuthError(webExchance)))
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.build();
}
/**
* @SO: This handle is called if the authentication fails with 401 or 403. But it's not called if an 500 internal
* server error is thrown.
*/
private Mono handleAuthError(ServerWebExchange webExchance) {
final ServerHttpResponse response = webExchance.getResponse();
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
response.getHeaders().setContentType(APPLICATION_JSON);
final var msg = "{\"foo\":\"bar\"}".getBytes(StandardCharsets.UTF_8);
return response.writeWith(Mono.just(response.bufferFactory().wrap(msg)));
}
}
Код: Выделить всё
Error has been observed at the following site(s):
*__checkpoint ⇢ AuthenticationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/foobar" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager.onError(JwtReactiveAuthenticationManager.java:81)
at reactor.core.publisher.Mono.lambda$onErrorMap$27(Mono.java:3785)
at reactor.core.publisher.Mono.lambda$onErrorResume$29(Mono.java:3875)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
[...]
Caused by: org.springframework.security.oauth2.jwt.JwtException: An error occurred while attempting to decode the Jwt:
at org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.lambda$decode$2(NimbusReactiveJwtDecoder.java:171)
at reactor.core.publisher.Mono.lambda$onErrorMap$27(Mono.java:3785)
at reactor.core.publisher.Mono.lambda$onErrorResume$29(Mono.java:3875)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
[...]
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "http://notavailable.io/realms/foobar/realms/my-realm"
at org.springframework.security.oauth2.jwt.ReactiveJwtDecoderProviderConfigurationUtils.lambda$getConfiguration$8(ReactiveJwtDecoderProviderConfigurationUtils.java:139)
at reactor.core.publisher.Flux.lambda$onErrorMap$28(Flux.java:7239)
at reactor.core.publisher.Flux.lambda$onErrorResume$29(Flux.java:7292)
[...]
Caused by: org.springframework.web.reactive.function.client.WebClientRequestException: Failed to resolve 'notavailable.io' [A(1)] after 4 queries
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:136)
Код: Выделить всё
private static Mono getConfiguration(String issuer, WebClient web, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\"";
return Flux.just(uris)
.concatMap((uri) -> web.get().uri(uri).retrieve().bodyToMono(STRING_OBJECT_MAP))
.flatMap((configuration) -> {
if (configuration.get("jwks_uri") == null) {
return Mono.error(() -> new IllegalArgumentException("The public JWK set URI must not be null"));
}
return Mono.just(configuration);
})
.onErrorContinue((ex) -> ex instanceof WebClientResponseException
&& ((WebClientResponseException) ex).getStatusCode().is4xxClientError(), (ex, object) -> {
})
.onErrorMap(RuntimeException.class,
(ex) -> (ex instanceof IllegalArgumentException) ? ex
: new IllegalArgumentException(errorMessage, ex))
.next()
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException(errorMessage)));
}
Если эмитент недоступен, клиент получает внутреннюю ошибку сервера 500 ответ с телом:
Код: Выделить всё
{
"timestamp": "2024-06-21T16:18:13.190+00:00",
"path": "/foobar",
"status": 500,
"error": "Internal Server Error",
"requestId": "2eeaa482-2"
}
Если эмитент недоступен, клиент получает внутреннюю ошибку сервера 500 ответ с телом:
Код: Выделить всё
{
"foo": "bar"
}
Кто-нибудь знает, как изменить ответ, когда URL-адрес эмитента недоступен?
Подробнее здесь: https://stackoverflow.com/questions/786 ... ebfluxsecu