Use HttpBasicConfigurer's Conneg Strategy

Closes gh-9100
This commit is contained in:
Josh Cummings 2020-10-06 15:32:41 -06:00
parent af669a2166
commit 4602e9a661
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
2 changed files with 72 additions and 2 deletions

View File

@ -16,12 +16,15 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
@ -48,8 +51,15 @@ import org.springframework.security.oauth2.server.resource.web.DefaultBearerToke
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
*
@ -130,6 +140,9 @@ import org.springframework.util.Assert;
public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
"X-Requested-With", "XMLHttpRequest");
private final ApplicationContext context;
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
@ -273,7 +286,25 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private void registerDefaultEntryPoint(H http) {
ExceptionHandlingConfigurer<H> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling != null) {
exceptionHandling.defaultAuthenticationEntryPointFor(this.authenticationEntryPoint, this.requestMatcher);
ContentNegotiationStrategy contentNegotiationStrategy = http
.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
MediaType.TEXT_XML);
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
MediaTypeRequestMatcher allMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);
allMatcher.setUseEquals(true);
RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.TEXT_HTML));
RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(
Arrays.asList(this.requestMatcher, X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));
exceptionHandling.defaultAuthenticationEntryPointFor(this.authenticationEntryPoint, preferredMatcher);
}
}

View File

@ -89,6 +89,10 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
@ -1108,7 +1112,8 @@ public class OAuth2ResourceServerConfigurerTests {
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
given(decoder.decode(anyString())).willThrow(JwtException.class);
// @formatter:off
MvcResult result = this.mvc.perform(get("/authenticated"))
MvcResult result = this.mvc.perform(get("/authenticated")
.header("Accept", "text/html"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn();
@ -1122,6 +1127,15 @@ public class OAuth2ResourceServerConfigurerTests {
assertThat(result.getRequest().getSession(false)).isNull();
}
@Test
public void unauthenticatedRequestWhenFormOAuth2LoginAndResourceServerThenNegotiates() throws Exception {
this.spring.register(OAuth2LoginAndResourceServerConfig.class, JwtDecoderConfig.class).autowire();
this.mvc.perform(get("/any").header("X-Requested-With", "XMLHttpRequest")).andExpect(status().isUnauthorized());
this.mvc.perform(get("/any").header("Accept", "application/json")).andExpect(status().isUnauthorized());
this.mvc.perform(get("/any").header("Accept", "text/html")).andExpect(status().is3xxRedirection());
this.mvc.perform(get("/any").header("Accept", "image/jpg")).andExpect(status().is3xxRedirection());
}
@Test
public void requestWhenDefaultAndResourceServerAccessDeniedHandlersThenMatchedByRequest() throws Exception {
this.spring
@ -1721,6 +1735,31 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class OAuth2LoginAndResourceServerConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.oauth2Login(withDefaults())
.oauth2ResourceServer((oauth2) -> oauth2
.jwt()
);
// @formatter:on
}
@Bean
ClientRegistrationRepository clients() {
ClientRegistration registration = TestClientRegistrations.clientRegistration().build();
return new InMemoryClientRegistrationRepository(registration);
}
}
@EnableWebSecurity
static class JwtHalfConfiguredConfig extends WebSecurityConfigurerAdapter {