mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-26 13:53:14 +00:00
ServerAuthenticationConverter should be configurable
Fixes gh-6186
This commit is contained in:
parent
63f2b6094f
commit
be423debfd
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.web.server;
|
package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
|
import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
|
||||||
|
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
|
||||||
|
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@ -155,10 +159,6 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
import org.springframework.web.server.WebFilterChain;
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
|
||||||
import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
|
|
||||||
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
|
|
||||||
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but for WebFlux.
|
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but for WebFlux.
|
||||||
* It allows configuring web based security for specific http requests. By default it will be applied
|
* It allows configuring web based security for specific http requests. By default it will be applied
|
||||||
@ -883,6 +883,7 @@ public class ServerHttpSecurity {
|
|||||||
public class OAuth2ResourceServerSpec {
|
public class OAuth2ResourceServerSpec {
|
||||||
private ServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();
|
private ServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();
|
||||||
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
|
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
|
||||||
|
private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
|
||||||
|
|
||||||
private JwtSpec jwt;
|
private JwtSpec jwt;
|
||||||
|
|
||||||
@ -915,6 +916,20 @@ public class ServerHttpSecurity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the {@link ServerAuthenticationConverter} to use for requests authenticating with
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
|
||||||
|
*
|
||||||
|
* @param bearerTokenConverter The {@link ServerAuthenticationConverter} to use
|
||||||
|
* @return The {@link OAuth2ResourceServerSpec} for additional configuration
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public OAuth2ResourceServerSpec bearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
|
||||||
|
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
|
||||||
|
this.bearerTokenConverter = bearerTokenConverter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public JwtSpec jwt() {
|
public JwtSpec jwt() {
|
||||||
if (this.jwt == null) {
|
if (this.jwt == null) {
|
||||||
this.jwt = new JwtSpec();
|
this.jwt = new JwtSpec();
|
||||||
@ -1003,8 +1018,6 @@ public class ServerHttpSecurity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void configure(ServerHttpSecurity http) {
|
protected void configure(ServerHttpSecurity http) {
|
||||||
ServerBearerTokenAuthenticationConverter bearerTokenConverter =
|
|
||||||
new ServerBearerTokenAuthenticationConverter();
|
|
||||||
this.bearerTokenServerWebExchangeMatcher.setBearerTokenConverter(bearerTokenConverter);
|
this.bearerTokenServerWebExchangeMatcher.setBearerTokenConverter(bearerTokenConverter);
|
||||||
|
|
||||||
registerDefaultAccessDeniedHandler(http);
|
registerDefaultAccessDeniedHandler(http);
|
||||||
@ -1083,7 +1096,7 @@ public class ServerHttpSecurity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class BearerTokenServerWebExchangeMatcher implements ServerWebExchangeMatcher {
|
private class BearerTokenServerWebExchangeMatcher implements ServerWebExchangeMatcher {
|
||||||
ServerBearerTokenAuthenticationConverter bearerTokenConverter;
|
ServerAuthenticationConverter bearerTokenConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<MatchResult> matches(ServerWebExchange exchange) {
|
public Mono<MatchResult> matches(ServerWebExchange exchange) {
|
||||||
@ -1092,7 +1105,7 @@ public class ServerHttpSecurity {
|
|||||||
.onErrorResume(e -> notMatch());
|
.onErrorResume(e -> notMatch());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBearerTokenConverter(ServerBearerTokenAuthenticationConverter bearerTokenConverter) {
|
public void setBearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
|
||||||
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
|
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
|
||||||
this.bearerTokenConverter = bearerTokenConverter;
|
this.bearerTokenConverter = bearerTokenConverter;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.config.web.server;
|
package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.hamcrest.core.StringStartsWith.startsWith;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
@ -27,14 +36,14 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
import okhttp3.mockwebserver.MockResponse;
|
|
||||||
import okhttp3.mockwebserver.MockWebServer;
|
|
||||||
import org.apache.http.HttpHeaders;
|
import org.apache.http.HttpHeaders;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
@ -56,10 +65,12 @@ import org.springframework.security.oauth2.core.OAuth2Error;
|
|||||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
||||||
|
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
|
||||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
@ -70,14 +81,8 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
|
|||||||
import org.springframework.web.reactive.DispatcherHandler;
|
import org.springframework.web.reactive.DispatcherHandler;
|
||||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
import static org.hamcrest.core.StringStartsWith.startsWith;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec}
|
* Tests for {@link org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec}
|
||||||
@ -225,6 +230,16 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
.expectStatus().isForbidden();
|
.expectStatus().isForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomBearerTokenServerAuthenticationConverterThenResponds() {
|
||||||
|
this.spring.register(CustomBearerTokenServerAuthenticationConverter.class, RootController.class).autowire();
|
||||||
|
|
||||||
|
this.client.get()
|
||||||
|
.cookie("TOKEN", this.messageReadToken)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenSignedAndCustomConverterThenConverts() {
|
public void getWhenSignedAndCustomConverterThenConverts() {
|
||||||
this.spring.register(CustomJwtAuthenticationConverterConfig.class, RootController.class).autowire();
|
this.spring.register(CustomJwtAuthenticationConverterConfig.class, RootController.class).autowire();
|
||||||
@ -429,6 +444,32 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableWebFlux
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
static class CustomBearerTokenServerAuthenticationConverter {
|
||||||
|
@Bean
|
||||||
|
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeExchange()
|
||||||
|
.anyExchange().hasAuthority("SCOPE_message:read")
|
||||||
|
.and()
|
||||||
|
.oauth2ResourceServer()
|
||||||
|
.bearerTokenConverter(bearerTokenAuthenticationConverter())
|
||||||
|
.jwt()
|
||||||
|
.publicKey(publicKey());
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ServerAuthenticationConverter bearerTokenAuthenticationConverter() {
|
||||||
|
return exchange -> Mono.justOrEmpty(exchange.getRequest().getCookies().getFirst("TOKEN").getValue())
|
||||||
|
.map(BearerTokenAuthenticationToken::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EnableWebFlux
|
@EnableWebFlux
|
||||||
@EnableWebFluxSecurity
|
@EnableWebFluxSecurity
|
||||||
static class CustomJwtAuthenticationConverterConfig {
|
static class CustomJwtAuthenticationConverterConfig {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user