mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-08 11:32:47 +00:00
Allow configuration of HTTP basic through nested builder
Issue: gh-5557 Fixes: gh-6885
This commit is contained in:
parent
3f2108921e
commit
12da990b6b
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2019 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface that accepts a single input argument and returns no result,
|
||||||
|
* with the ability to throw a (checked) exception.
|
||||||
|
*
|
||||||
|
* @author Eleftheria Stein
|
||||||
|
* @param <T> the type of the input to the operation
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Customizer<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the customizations on the input argument.
|
||||||
|
*
|
||||||
|
* @param t the input argument
|
||||||
|
* @throws Exception if any error occurs
|
||||||
|
*/
|
||||||
|
void customize(T t) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Customizer} that does not alter the input argument.
|
||||||
|
*
|
||||||
|
* @return a {@link Customizer} that does not alter the input argument.
|
||||||
|
*/
|
||||||
|
static <T> Customizer<T> withDefaults() {
|
||||||
|
return t -> {};
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -19,6 +19,7 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||||
@ -1094,6 +1095,41 @@ public final class HttpSecurity extends
|
|||||||
return getOrApply(new HttpBasicConfigurer<>());
|
return getOrApply(new HttpBasicConfigurer<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures HTTP Basic authentication.
|
||||||
|
*
|
||||||
|
* <h2>Example Configuration</h2>
|
||||||
|
*
|
||||||
|
* The example below demonstrates how to configure HTTP Basic authentication for an
|
||||||
|
* application. The default realm is "Realm", but can be
|
||||||
|
* customized using {@link HttpBasicConfigurer#realmName(String)}.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class HttpBasicSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeRequests()
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* .and()
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param httpBasicCustomizer the {@link Customizer} to provide more options for
|
||||||
|
* the {@link HttpBasicConfigurer}
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public HttpSecurity httpBasic(Customizer<HttpBasicConfigurer<HttpSecurity>> httpBasicCustomizer) throws Exception {
|
||||||
|
httpBasicCustomizer.customize(getOrApply(new HttpBasicConfigurer<>()));
|
||||||
|
return HttpSecurity.this;
|
||||||
|
}
|
||||||
|
|
||||||
public <C> void setSharedObject(Class<C> sharedType, C object) {
|
public <C> void setSharedObject(Class<C> sharedType, C object) {
|
||||||
super.setSharedObject(sharedType, object);
|
super.setSharedObject(sharedType, object);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
@ -91,6 +92,37 @@ public class HttpBasicConfigurerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void httpBasicWhenUsingDefaultsInLambdaThenResponseIncludesBasicChallenge() throws Exception {
|
||||||
|
this.spring.register(DefaultsLambdaEntryPointConfig.class).autowire();
|
||||||
|
|
||||||
|
this.mvc.perform(get("/"))
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
.andExpect(header().string("WWW-Authenticate", "Basic realm=\"Realm\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class DefaultsLambdaEntryPointConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.httpBasic(withDefaults());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
auth
|
||||||
|
.inMemoryAuthentication();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//SEC-2198
|
//SEC-2198
|
||||||
@Test
|
@Test
|
||||||
public void httpBasicWhenUsingDefaultsThenResponseIncludesBasicChallenge() throws Exception {
|
public void httpBasicWhenUsingDefaultsThenResponseIncludesBasicChallenge() throws Exception {
|
||||||
|
@ -38,6 +38,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
@ -102,6 +103,36 @@ public class NamespaceHttpBasicTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicAuthenticationWhenUsingDefaultsInLambdaThenMatchesNamespace() throws Exception {
|
||||||
|
this.spring.register(HttpBasicLambdaConfig.class, UserConfig.class).autowire();
|
||||||
|
|
||||||
|
this.mvc.perform(get("/"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "invalid")))
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Realm\""));
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "password")))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class HttpBasicLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().hasRole("USER")
|
||||||
|
.and()
|
||||||
|
.httpBasic(withDefaults());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http@realm equivalent
|
* http@realm equivalent
|
||||||
*/
|
*/
|
||||||
@ -127,6 +158,30 @@ public class NamespaceHttpBasicTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicAuthenticationWhenUsingCustomRealmInLambdaThenMatchesNamespace() throws Exception {
|
||||||
|
this.spring.register(CustomHttpBasicLambdaConfig.class, UserConfig.class).autowire();
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "invalid")))
|
||||||
|
.andExpect(status().isUnauthorized())
|
||||||
|
.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Custom Realm\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class CustomHttpBasicLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().hasRole("USER")
|
||||||
|
.and()
|
||||||
|
.httpBasic(httpBasicConfig -> httpBasicConfig.realmName("Custom Realm"));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http/http-basic@authentication-details-source-ref equivalent
|
* http/http-basic@authentication-details-source-ref equivalent
|
||||||
*/
|
*/
|
||||||
@ -161,6 +216,40 @@ public class NamespaceHttpBasicTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicAuthenticationWhenUsingAuthenticationDetailsSourceRefInLambdaThenMatchesNamespace()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.register(AuthenticationDetailsSourceHttpBasicLambdaConfig.class, UserConfig.class).autowire();
|
||||||
|
|
||||||
|
AuthenticationDetailsSource<HttpServletRequest, ?> source =
|
||||||
|
this.spring.getContext().getBean(AuthenticationDetailsSource.class);
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "password")));
|
||||||
|
|
||||||
|
verify(source).buildDetails(any(HttpServletRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class AuthenticationDetailsSourceHttpBasicLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource =
|
||||||
|
mock(AuthenticationDetailsSource.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.httpBasic(httpBasicConfig ->
|
||||||
|
httpBasicConfig.authenticationDetailsSource(this.authenticationDetailsSource));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {
|
||||||
|
return this.authenticationDetailsSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http/http-basic@entry-point-ref
|
* http/http-basic@entry-point-ref
|
||||||
*/
|
*/
|
||||||
@ -195,4 +284,38 @@ public class NamespaceHttpBasicTests {
|
|||||||
.authenticationEntryPoint(this.authenticationEntryPoint);
|
.authenticationEntryPoint(this.authenticationEntryPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicAuthenticationWhenUsingEntryPointRefInLambdaThenMatchesNamespace() throws Exception {
|
||||||
|
this.spring.register(EntryPointRefHttpBasicLambdaConfig.class, UserConfig.class).autowire();
|
||||||
|
|
||||||
|
this.mvc.perform(get("/"))
|
||||||
|
.andExpect(status().is(999));
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "invalid")))
|
||||||
|
.andExpect(status().is(999));
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")
|
||||||
|
.with(httpBasic("user", "password")))
|
||||||
|
.andExpect(status().isNotFound());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class EntryPointRefHttpBasicLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
AuthenticationEntryPoint authenticationEntryPoint =
|
||||||
|
(request, response, ex) -> response.setStatus(999);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().hasRole("USER")
|
||||||
|
.and()
|
||||||
|
.httpBasic(httpBasicConfig ->
|
||||||
|
httpBasicConfig.authenticationEntryPoint(this.authenticationEntryPoint));
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user