Author Archives: Nicolas Riousset

Fixing Mockito UnnecessaryStubbingException with JUnit5

After upgrading to JUnit 5, you may bump into some unit tests now failing with the following error :

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):...

JUnit 5 is tricter regarding stubbing. As explained in the error message, unnecessary stubbing is not a good practice. It usually results from copy and paste, and creates confusion about the tested behaviour. In such a situation, you just want to remove the unnecessary stubbing.

However, there are some cases when you’d like a less strict approach. For example, when you use a helper method that does some mocking that is required by most of the cases, but not all. Than you have to choices :

  • instead of mocking directly with “when(…).then(…)”, prefix the call with lenient(). Ex
    lenient().when(oauth.getAuthorities()).thenReturn(authorities);
  • else, annotate your test class with :
    @MockitoSettings(strictness = Strictness.LENIENT)
    It will relax the mockito stubbing rules.

Increasing uploaded files maximum size in a Spring Boot 2 application

When uploading a file to a SpringBoot Application, you will get the following exception for files over 1 Mb size :

Maximum upload size exceeded; nestedexception is java.lang.IllegalStateException:org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field fileexceeds its maximum permitted size of1048576 bytes.

These are the default Spring Boot limits. To increase these limits to 10 Mb, with Spring Boot 2, add the following lines to your application.properties file:

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

Apache as a reverse proxy for Tomcat on Debian 10

Here’s the situation: you have an Angular application that you want to host on apache, and a Spring Boot application running with its embedded tomcat, or that you want t o deploy on tomcat. However, you don’t want to expose your tomcat default 8080 or 8443 ports.

The solution is to use Apache a reverse proxy for tomcat. Only the apache standard http and https ports will be exposed, and you only need to deal with https certificates at the apache level.

To achieve this :

1. Enable the apache proxy mod

a2enmod proxy
a2enmod proxy_http
systemctl restart apache2

2. Configure apache as a proxy

Edit /etc/apache2/sites-enabled/default-ssl.conf

In the following examples, all traffic to /api will be redirected to the localhost tomcat /api endpoint.

If your tomcat server uses https, then you must enable Apache as a https proxy, and mostlikely disable all https security verification because you will access the tomcat using your localhost ip address, which won’t match the url of the https certifcate :

<VirtualHost *:443>
    ...
    SSLProxyEngine on
    SSLProxyVerify none
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off
    SSLProxyCheckPeerExpire off
    ProxyRequests On
    ProxyPreserveHost On
    ProxyPass /api https://127.0.0.1:8443/api
    ProxyPassReverse /api https://127.0.0.1:8443/api
 
    <Location "/api">
        Order allow,deny
        Allow from all
    </Location>
    ...
</VirtualHost>

If your tomcat is using plain old http, and not https, then a simpler configuration will be enough :

<VirtualHost *:443>
    ...
    ProxyRequests On
    ProxyPreserveHost On
    ProxyPass /api http://127.0.0.1:8080/api
    ProxyPassReverse /api http://127.0.0.1:8080/api
 
    <Location "/api">
        Order allow,deny
        Allow from all
    </Location>
    ...
</VirtualHost>

403 response to a CORS preflight request from an angular app to a spring security REST API caused by a Access-Control-Request-Headers mismatch

CORS is a pain, I’ve been struggling with a POST to an API whose CORS preflight OPTIONS request was rejected with a 403.

I was going crazy because while the OPTIONS request was rejected when executed from the navigator, it succeeded when executed from the command line using CURL.

curl -v -H "Access-Control-Request-Method: POST" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/api/v1/documents/upload

 Trying ::1…
 TCP_NODELAY set
 Connected to localhost (::1) port 8080 (#0) 
   OPTIONS /api/v1/documents/upload HTTP/1.1
   Host: localhost:8080
   User-Agent: curl/7.55.1
   Accept: /
   Access-Control-Request-Method: POST
   Origin: http://localhost:4200
   < HTTP/1.1 200
   < Vary: Origin
   < Vary: Access-Control-Request-Method
   < Vary: Access-Control-Request-Headers
   < Access-Control-Allow-Origin: *
   < Access-Control-Allow-Methods: POST
   < X-Content-Type-Options: nosniff
   < X-XSS-Protection: 1; mode=block
   < Cache-Control: no-cache, no-store, max-age=0, must-revalidate
   < Pragma: no-cache
   < Expires: 0
   < X-Frame-Options: DENY
   < Referrer-Policy: origin
   < Content-Length: 0
   < Date: Thu, 18 Mar 2021 22:27:09 GMT
   <
      Connection #0 to host localhost left intact    

This had to be an issue with the browser cache, even though it was weird that it was affecting both Firefox and Chrome, both in normal and private sessions.
But then I started to ensure the CURL request was sending exactly the same headers, and I eventually identified the issue.

The browser, whether Firefox or Chrome, was adding a “Access-Control-Request-Headers: x-requested-with” header. And this header requires a response from the server indicating that it will accept a request with this header. And my Spring Security config was not allowing the “x-requested-with” header:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
            .and().csrf().disable()
            .authorizeRequests()
                .anyRequest().access("isAuthenticated() or hasIpAddress('127.0.0.1/24') or hasIpAddress('::1')")
            .and().httpBasic().and().headers().referrerPolicy(ReferrerPolicy.ORIGIN);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOriginPattern(CorsConfiguration.ALL);
        configuration.setAllowedMethods(List.of(CorsConfiguration.ALL));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }    
}

Changing the setAllowedHeaders line to “configuration.setAllowedHeaders(List.of(CorsConfiguration.ALL)); ” eventually fixed the issue.