parent
af669a2166
commit
4602e9a661
|
|
@ -16,12 +16,15 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.resource;
|
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 java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
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.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
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.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
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>>
|
public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
|
extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
|
||||||
|
|
||||||
|
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
|
||||||
|
"X-Requested-With", "XMLHttpRequest");
|
||||||
|
|
||||||
private final ApplicationContext context;
|
private final ApplicationContext context;
|
||||||
|
|
||||||
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
||||||
|
|
@ -273,7 +286,25 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||||
private void registerDefaultEntryPoint(H http) {
|
private void registerDefaultEntryPoint(H http) {
|
||||||
ExceptionHandlingConfigurer<H> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
ExceptionHandlingConfigurer<H> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||||
if (exceptionHandling != null) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
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.DefaultOAuth2AuthenticatedPrincipal;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
|
|
@ -1108,7 +1112,8 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||||
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
|
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
|
||||||
given(decoder.decode(anyString())).willThrow(JwtException.class);
|
given(decoder.decode(anyString())).willThrow(JwtException.class);
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
MvcResult result = this.mvc.perform(get("/authenticated"))
|
MvcResult result = this.mvc.perform(get("/authenticated")
|
||||||
|
.header("Accept", "text/html"))
|
||||||
.andExpect(status().isFound())
|
.andExpect(status().isFound())
|
||||||
.andExpect(redirectedUrl("http://localhost/login"))
|
.andExpect(redirectedUrl("http://localhost/login"))
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
@ -1122,6 +1127,15 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||||
assertThat(result.getRequest().getSession(false)).isNull();
|
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
|
@Test
|
||||||
public void requestWhenDefaultAndResourceServerAccessDeniedHandlersThenMatchedByRequest() throws Exception {
|
public void requestWhenDefaultAndResourceServerAccessDeniedHandlersThenMatchedByRequest() throws Exception {
|
||||||
this.spring
|
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
|
@EnableWebSecurity
|
||||||
static class JwtHalfConfiguredConfig extends WebSecurityConfigurerAdapter {
|
static class JwtHalfConfiguredConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue