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