Reputation: 8614
I tried using FORCE_HTTPS=true
and SECURITY_REQUIRE_SSL=true
as config variables, but neither works. I know that the former is supported by Cloud Foundry, but I've confirmed with Heroku that they do not support it. The SECURITY_REQUIRE_SSL
property is supported by Spring Boot, but maybe only for basic auth?
Upvotes: 4
Views: 2836
Reputation: 39
If you're using spring boot, you have to add dependency to your project
for maven
<dependency>
<groupId>io.github.createam-labs</groupId>
<artifactId>spring-boot-starter-heroku</artifactId>
<version>1.1</version>
</dependency>
or for gradle
compile('io.github.createam-labs:spring-boot-starter-heroku:1.1')
and then enable https enforcing in application.properties
file by adding following line
heroku.enforceHttps=true
For more information you can visit my project site https://github.com/createam-labs/spring-boot-starter-heroku
Upvotes: 2
Reputation: 25387
According to the docs on heroku this should be all you need:
@Configuration
public class WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure();
}
}
Force the use of HTTPS
Unless you have very specific needs, your app should be using HTTPS for all requests. Heroku provides an HTTPS URL (in the form https://.herokuapp.com) for every app, as well as free tools for adding your own domains and certificates.
You can enforce the use of HTTPS when your app is running on Heroku by adding the following configuration to your Spring Boot app.
Upvotes: 3
Reputation: 8614
I was able to fix this by creating an HttpEnforcer
filter:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HttpsEnforcer implements Filter {
private FilterConfig filterConfig;
public static final String X_FORWARDED_PROTO = "x-forwarded-proto";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getHeader(X_FORWARDED_PROTO) != null) {
if (request.getHeader(X_FORWARDED_PROTO).indexOf("https") != 0) {
String pathInfo = (request.getPathInfo() != null) ? request.getPathInfo() : "";
response.sendRedirect("https://" + request.getServerName() + pathInfo);
return;
}
}
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
// nothing
}
}
And registering it in an existing @Configuration
class.
@Bean
public Filter httpsEnforcerFilter(){
return new HttpsEnforcer();
}
This is different from the solution I posted in the comments above because of the null check for pathInfo. Without this, it still works, but the Location
does show up with null
at the end.
$ curl -i http://www.21-points.com
HTTP/1.1 302 Found
Server: Cowboy
Connection: keep-alive
Expires: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
X-Xss-Protection: 1; mode=block
Pragma: no-cache
Location: https://www.21-points.comnull
Date: Tue, 31 Oct 2017 14:33:26 GMT
X-Content-Type-Options: nosniff
Content-Length: 0
Via: 1.1 vegur
Upvotes: 11