While upgrading my Spring Boot applications through the 2.0.0 milestone releases, I've noticed that starting in 2.0.0.M4 security.require-ssl
and other security configuration options are gone. I didn't find any mention of deprecations or a new approach in the current docs so I dug around and found the GitHub issue where the work originated. I applaud the goal in the GitHub issue to:
significantly simplify security configuration in 2.0.
and am happy change my patterns to upgrade, but I'm a little stuck on how to require SSL in specific environments. I know I can accomplish a similar outcome in my WebSecurityConfigurerAdapter
configuration using http.requiresChannel().anyRequest().requiresSecure()
but I don't want to enable this setting in every environment I run my applications in (e.g. pre-production environments without a certificate, local development on localhost). I know I could put some conditional logic in my WebSecurityConfigurerAdapter
configuration but I like to keep my Spring configurations "environment agnostic" and keep my environment specific configurations in properties files specific to a profile.
After these recent simplifying changes to Spring Boot's security configuration, what's a recommended approach to require SSL in some environments?
There are a few options depending on your setup.
Changing the RequestMatcher
If the only time you require HTTPS is in an environment with a custom header injected by a Proxy (i.e. X-Forwarded-* headers), you can do something like this:
http
.requiresChannel()
.requestMatchers( r -> r.getHeader("X-Forwarded-Proto") != null).requiresSecure()
.and()
// ...
The nice thing about this approach is that you do not need to do anything to turn HTTPS on or off. The downside is that there is a very minimal performance hit of comparing the headers. This performance hit should be negligible, but may not be aesthetically pleasing to some.
Conditional Configuration
Since this is Java Code you can always have a boolean flag that determines if you require HTTPS or not. For example, create a member variable and resolve it to Externalized Configuration with something like this:
@Value("${security.require_ssl}")
private boolean requireHttps;
Then use something like this:
if(this.requireHttps) {
http
.requiresChannel()
.anyRequest().requiresSecure();
}
http
// ...
This approach aligns nicely with Spring Boot 1.x approach and it does not suffer from the additional if statement in each request. However, it requires a property to be managed.
Profiles
As mentioned by Madhura you can also use @Profile
. I find this approach is better suited if you have lots of differences between environments and not as ideal for something minor like determining if HTTPS is required. The problem with this approach when making a small change is you run into a lot of duplication.
You're absolutely right about adding your own WebSecurityConfigurerAdapter
for customizing security configuration. To keep that configuration environment agnostic, you can add an @Profile
so that it can be switched on and off depending on the active profile. If it is only the ssl configuration that is environment agnostic, and the rest of the security configuration is the same for all environments, you can add a WebSecurityConfigurerAdapter
that only configures http.requiresChannel().anyRequest().requiresSecure()
that has the @Profile
on it and order it such that it is the first one that kicks in.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With