I'm developing a service with spring webflux. I implemented exception handling using #ControllerAdvice. It works pretty well, but when I run integration tests it seems that #ControllerAdvice annotated component is not loaded, resulting in this response:
{
"timestamp":"2019-11-28T08:56:47.285+0000",
"path":"/fooController/bar",
"status":500,
"error":"Internal Server Error",
"message":"java.lang.IllegalStateException: Could not resolve parameter [1] in protected org.springframework.http.ResponseEntity<it.test.model.Response> it.test.exception.ExceptionHandlerController.handleServiceException(java.lang.Exception,org.springframework.web.context.request.WebRequest): No suitable resolver
}
This is my controller advice:
#ControllerAdvice
public class ExceptionHandlerController extends ResponseEntityExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(ExceptionHandlerController.class);
#ExceptionHandler
protected ResponseEntity<Response> handleServiceException(Exception ex, WebRequest request) {
this.logger.error("Error occurred: \"{}\"", ex.getMessage());
Response<Foo> response = new Response<>(new Foo(),
"generic error",
HttpStatus.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(response, null, HttpStatus.OK);
}
}
And this is my integration test class
#ExtendWith(SpringExtension.class)
#WebFluxTest(MyController.class)
#ContextConfiguration(classes = {MyController.class, MyServiceImpl.class, ExceptionHandlerController.class })
public class MyControllerIT {
#Autowired
private WebTestClient webTestClient;
#Test
public void testShouldFail() throws IOException {
return this.webTestClient.get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.statusCode").isEqualTo(500);
}
}
if you read the documentation for #WebFluxTest it states:
Annotation that can be used for a Spring WebFlux test that focuses
only on Spring WebFlux components.
Using this annotation will disable full auto-configuration and instead
apply only configuration
relevant to WebFlux tests (i.e. #Controller, #ControllerAdvice,
#JsonComponent, Converter/GenericConverter, and WebFluxConfigurer
beans but not #Component, #Service or #Repository beans).
Typically #WebFluxTest is used in combination with #MockBean or
#Import to create any collaborators required by your #Controller
beans.
If you are looking to load your full application configuration and use
WebTestClient, you should consider #SpringBootTest combined with
#AutoConfigureWebTestClient rather than this annotation.
This means that
#ContextConfiguration(classes = {MyController.class, MyServiceImpl.class, ExceptionHandlerController.class })
is not what u use here. The #WebFluxTest annotation does not load #Component, #Service or #Repository
It is mainly used to test RestControllers only, and their advices.
The options you have seem to be:
loading MyController.class and then mock any dependency this class has (MyServiceImpl.class)
load a full Context using #SpringBootTest instead combined with
#AutoConfigureWebTestClient
Just a not on top of the already accepted answer which is totally correct.
The usage of #ContextConfiguration is necessary only for testing Functional endpoints. I Think this is probably what confuses people.
#WebFluxTest is used during functional endpoint tests for starting the web context and server.
But since we dont use controllers, we have to use #ContextConfiguration to bring the handler and routerFunction beans in our spring context.
In this case since we use annotated controller a simple #WebFluxTest(Controller.class) is more than enough for unit testing.
Adding controllerAdvice in the WebTestClient solved the issue.
WebTestClient.bindToController(controller)
.controllerAdvice(errorControllerAdvice)
.build()
I am writing an integration test where the application has a basic auth applied with spring security. I am using #WithMockUser to tell mockMVC how to authenticate with my endpoints.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#WithMockUser(username="username", password="password")
This works but I am wondering if I can replace those strings with references to the values in my application.properties file similiar how you can do:
#Value("${spring.security.user.name}")
private String userName;
Is the above possible?
I ended up removing the #WithMockUser annotation and went with this solution which worked better for my use case. This method authenticates with the configured basic auth so going forward if it was to change it wouldn't be a problem.
#RunWith(SpringRunner.class)
#SpringBootTest
public class sampleTestIT {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = webAppContextSetup(webApplicationContext).build();
}
....
}
You can pass only constant expressions to annotations but an alternative will be to create your own Security Context as explained here in the #WithSecurityContext chapter:
https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/test-method.html
I would also create my own test properties configuration file which will be used to read properties from the tests.
I want to use Spring Security for JWT authentication. But it comes with default authentication. I am trying to disable it, but the old approach of doing this - disabling it through application.properties - is deprecated in 2.0.
This is what I tried:
#Configuration
public class StackWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
// http.authorizeRequests().anyRequest().permitAll(); // Also doesn't work.
}
}
How can I simply disable basic security?
UPDATE
It might be nice to know that I am not using web mvc but web flux.
Screenshot:
According to the new updates in Spring 2.0, if Spring Security is on the classpath, Spring Boot will add #EnableWebSecurity.So adding entries to the application.properties ain't gonna work (i.e it is no longer customizable that way). For more information visit the official website Security changes in Spring Boot 2.0
Albeit not sure about your requirement exactly, I could think of one workaround like the following:-
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/").permitAll();
}
}
Hope this helps.
From Spring Boot 2.1 on, if you include spring-boot-actuator, it does not suffice anymore to only exclude SecurityAutoconfiguration, you also need to exclude ManagementWebSecurityAutoConfiguration, like so:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class })
According to the reference documentation, the Security configuration for allowing all requests with WebFlux should look like this:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().permitAll();
return http.build();
}
}
This worked for me:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
}
You can add/modify the following to your Application class:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class MyApplication {
}
Adding some fresh answer, I assume all use actuator, if not I'd bet one class exclusion should be sufficient, I managed to disable through properties:
spring:
autoconfigure:
exclude: ${spring.autoconfigure.sac}, ${spring.autoconfigure.mwsas}
sac: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
mwsas: org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
I've referenced two auto-config classes through property to keep the length intact (note that IntelliJ Ultimate will cry if you reference it like that as it has no clue what are these placeholder values and if they are actually legit classes, so inline if that annoys you).
Application however does not fail to start as claimed by:
https://www.baeldung.com/spring-boot-security-autoconfiguration
if you just disable SecurityAutoConfiguration
If it did work, you will stop seeing auto generated password and it is a little bit less confusing than the accepted answer, as dev reading the log won't get confused by generated password for basic auth while security allows all.
Why just disabling main auto config class isn't enough is because of this fella:
#Configuration
class ManagementWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(
EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))
.permitAll().anyRequest().authenticated().and().formLogin().and()
.httpBasic();
}
}
There was tons of work made to split actuator and security config which confused us all, now its more straightforward but artifacts like these still exist. Spring devs will correct me if I am wrong :-).
I have leveraged #ConditionalOnProperty to load the following SecurityConfig.java class if I set spring.security.enabled property to false in my application.yml to disable spring security and it works like a charm.
#ConditionalOnProperty(name = "spring.security.enabled", havingValue = "false")
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/").permitAll();
}
}
If anyone is struggling with this in a WebFlux based application, or a Spring Cloud Gateway application, the below worked for me:
#EnableWebFluxSecurity
public class InsecurityConfiguration {
// #formatter:off
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange().permitAll();
return http.build();
}
}
To disable default security for Spring Boot Reactive Web applications, use the following excludes when you have actuator also in the classpath.
#SpringBootApplication(exclude = {ReactiveSecurityAutoConfiguration.class, ReactiveManagementWebSecurityAutoConfiguration.class })
I think what you are looking for is to override the default authentication entry point which is set to BasicAuthenticationEntryPoint.
This entrypoint adds the
"WWW-Authenticate": "Basic realm=..."
header that tells your browser to use Basic Auth.
If you're extending WebSecurityConfigurerAdapter, you can pass in true to the super constructor to disable the defaults.
You may need to provide other beans if you do this.
/**
* Creates an instance which allows specifying if the default configuration should be
* enabled. Disabling the default configuration should be considered more advanced
* usage as it requires more understanding of how the framework is implemented.
*
* #param disableDefaults true if the default configuration should be disabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
this.disableDefaults = disableDefaults;
}
If you want to disable it just for testing purposes -
Rather than completely disabling the auto-configuration, I create an "InsecurityConfiguration" in addition to "SecurityConfiguration", and activate it with either a Spring Profile or Property value.
Technically security is still configured, but wide open.
#Configuration
#ConditionalOnProperty(prefix = "security", value = "disabled", havingValue = "true")
public class InsecurityConfiguration extends WebSecurityConfigurerAdapter {
private final static Logger log = LoggerFactory.getLogger(InsecurityConfiguration.class);
#Override
protected void configure(HttpSecurity http) throws Exception {
log.warn("configuring insecure HttpSecurity");
http.authorizeRequests().anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
log.warn("configuring insecure WebSecurity");
web.ignoring().antMatchers("/**");
}
}
Note This is for mvc, not webflux. For Webflux you should create a SecurityWebFilterChain like Bryan mentioned.
This is how I generally disable basic auth in webflux, when using JWT -
#Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
http
.authorizeExchange().anyExchange().authenticated().and()
.httpBasic().disable()
.formLogin().disable()
.logout().disable()
.oauth2ResourceServer()
.jwt()
.and()
.and().exceptionHandling().accessDeniedHandler(problemSupport);
return http.build();
}
Only properties - works for me (sb2 - 2022):
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
- org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
Simple solution for Spring Boot 2.6
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
In Spring boot 2, there is no way to disable basic authentication by application.properties file. But the only thing is use annotation
#EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
in the main class.
It works
The problem is with org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter
it has private ServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();
so to fix it during ServerHttpSecurity initialization add:
http.exceptionHandling().authenticationEntryPoint(HttpStatusServerEntryPoint(HttpStatus.FORBIDDEN))
Looks like vanilla (servlet) spring uses org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer#createDefaultEntryPoint
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if (this.defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if (this.defaultEntryPointMappings.size() == 1) {
return this.defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
this.defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
.next());
return entryPoint;
}
Side note: mutable fields in builder style beans (like ExceptionTranslationWebFilter) make spring code hard to debug (too magic configuration as well)
You should add #EnableWebSecurity to enable a custom security configuration.
After that simply disable the form login
#Configuration
#EnableWebSecurity
public class StackWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().disable();
}
}
This worked for me
#SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class})
class SpringApplication{
...
}
I am trying to to rest my rest classes in Spring MVC
If I run the following code (ran fine when the project was small but now fails) it tries to load all the different components in my application.
This includes beans which interact with external systems and need credentials in order to connect
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class TestDummyRest extends BaseRestTestCase{
#Autowired
private MockMvc mockMvc;
#MockBean
private IDummyServices mockDummyServices;
#Test
public void getSendGoodMessage() throws Exception {
given(mockDummyServices.sendGoodMessage(Mockito.anyString())).willReturn(true);
mockMvc.perform(get("/dummy"))
.andExpect(status().isOk())
.andExpect(content().contentType(TEXT_PLAIN_CONTENT_TYPE));
verify(mockDummyServices, times(1)).sendGoodMessage(Mockito.anyString());
}
}
How do I tell my test classes not to load the #Configuration or #Component classes of my application?
Instead of not creating other classes in your application, you could only create the classes you are interested in, see 15.6.1 Server-Side Tests - Setup Options
The second is to simply create a controller instance manually without
loading Spring configuration. Instead basic default configuration,
roughly comparable to that of the MVC JavaConfig or the MVC namespace,
is automatically created and can be customized to a degree:
public class MyWebTests {
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
// ...
}
You need to use #TestComponent and #TestConfiguration for this as explained in Spring doc here
I am trying to update my test cases to use the #DataJpaTest. However, I am encountering some issues that appear to be related to Spring Security. The following is an example of the test class.
#RunWith(SpringRunner.class)
#DataJpaTest
public class ExampleRepositoryTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
#Inject
private ExampleRepository repository;
#Test
public void test() throws Exception {
...
}
I keep getting the error java.lang.IllegalStateException: Failed to load ApplicationContext due to the missing bean org.springframework.security.config.annotation.ObjectPostProcessor.
The project is a RESTful application with Spring security. The original test case created a full Spring Boot context using #SpringBootTest. The #DataJpaTest is supposed to help me test the JPA slice, which is exactly what I want.
Any help would be greatly appreciated. What am I missing?
I got the same error.
for my case, I have added #SpringBootApplication and #EnableResourceServer on the same class.
when I move #EnableResourceServer to another configuration class, the error is gone.