Reputation: 555
For Spring Boot based application I have configurared ssl properties at application.properties, see my configuration here:
server.port=8443
server.ssl.key-alias=tomcat
server.ssl.key-password=123456
server.ssl.key-store=classpath:key.p12
server.ssl.key-store-provider=SunJSSE
server.ssl.key-store-type=pkcs12
And I have added conection at Application.class, like
@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
final TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addAdditionalTomcatConnectors(this.createConnection());
return factory;
}
private Connector createConnection() {
final String protocol = "org.apache.coyote.http11.Http11NioProtocol";
final Connector connector = new Connector(protocol);
connector.setScheme("http");
connector.setPort(9090);
connector.setRedirectPort(8443);
return connector;
}
But when I try the following by
http://127.0.0.1:9090/
redirect to
https://127.0.0.1:8443/
is not performed. Who faced a similar problem?
Upvotes: 50
Views: 120186
Reputation: 116031
For Tomcat to perform a redirect, you need to configure it with one or more security constraints. You can do this by post-processing the Context
using a TomcatEmbeddedServletContainerFactory
subclass.
For example:
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
Due to CONFIDENTIAL
and /*
, this will cause Tomcat to redirect every request to HTTPS. You can configure multiple patterns and multiple constraints if you need more control over what is and is not redirected.
An instance of the above TomcatEmbeddedServletContainerFactory
subclass should be defined as a bean using a @Bean
method in a @Configuration
class.
Upvotes: 40
Reputation: 13509
These all seem way complicated. Why don't we just add an interceptor that checks the port and if it's port 80, redirect it to the same url, but prefixed with https:// instead.
@Component
public class HttpsConfig implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// String requestedPort = request.getServerPort() if you're not behind a proxy
String requestedPort = request.getHeader("X-Forwarded-Port"); // I'm behind a proxy on Heroku
if (requestedPort != null && requestedPort.equals("80")) { // This will still allow requests on :8080
response.sendRedirect("https://" + request.getServerName() + request.getRequestURI() + (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
return false;
}
return true;
}
}
and don't forget to register your lovely interceptor
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpsConfig());
}
}
Note: Your production web server will typically run on 1 or 2 ports (80 unsecure 443 secure), and you should know what they are, so I don't see this as much of a security risk allowing other ports.
Upvotes: 1
Reputation: 157
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
}
}
If your application is behind a load balancer or reverse proxy server you will need to add the following to your application.properties file:
server.forward-headers-strategy=NATIVE
This will prevent an redirect loop.
If you are using Tomcat you can configure the names of the forward headers in your application.properties file:
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
See the Spring Boot Documentation for more information.
Upvotes: 6
Reputation: 461
In Spring-Boot, need below dependency
Step 1-
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Step 2- Just need to do below configurations on application.properties file
- server.port=8443
- server.ssl.key.alias=ode-https
- server.ssl.key-store-type=JKS (just for testing i USED JSK, but for production normally use pkcs12)
- server.ssl.key-password=password
- server.ssl.key-store=classpath:ode-https.jks
Step 3- now need to generate a certificate using the above details.
keytool -genkey -alias ode-https -storetype JKS -keyalg RSA -keys ize 2048 -validity 365 -keystore ode-https.jks
Step 4- move the certificate to resources folder in your program.
Step 5- Create config class
@Configuration
public class HttpsConfiguration {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
@Value("${server.port.http}") //Defined in application.properties file
int httpPort;
@Value("${server.port}") //Defined in application.properties file
int httpsPort;
private Connector redirectConnector() {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(httpPort);
connector.setSecure(false);
connector.setRedirectPort(httpsPort);
return connector;
}
}
that's it.
Upvotes: 20
Reputation: 24472
Since TomcatEmbeddedServletContainerFactory
has been removed in Spring Boot 2, use this:
@Bean
public TomcatServletWebServerFactory httpsRedirectConfig() {
return new TomcatServletWebServerFactory () {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
}
Upvotes: 5
Reputation: 2452
Follow only 2 steps.
1- Add spring security dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2- Add this class on root package of your application.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
}
}
Upvotes: 16
Reputation: 3165
The approved answer was not enough for me.
I had to also add the following to my web security config, as I am not using the default 8080 port:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private Environment environment;
@Override
public void configure(HttpSecurity http) throws Exception {
// other security configuration missing
http.portMapper()
.http(Integer.parseInt(environment.getProperty("server.http.port"))) // http port defined in yml config file
.mapsTo(Integer.parseInt(environment.getProperty("server.port"))); // https port defined in yml config file
// we only need https on /auth
http.requiresChannel()
.antMatchers("/auth/**").requiresSecure()
.anyRequest().requiresInsecure();
}
}
Upvotes: 14
Reputation: 1470
Setting this property on your application*.properties file (and the corresponding servlet-specific configuration for HTTPS headers in case you are running behind a proxy) and having Spring Security set-up (e.g. having org.springframework.boot:spring-boot-starter-security on your classpath) should be enough:
security.require-ssl=true
Now, for some reason that configuration is not honored when basic authentication is disabled (at least on old versions of Spring Boot). So in that case you would need to take an extra step and honor it yourself by manually configuring the security on your code, like this:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Inject private SecurityProperties securityProperties;
@Override
protected void configure(HttpSecurity http) throws Exception {
if (securityProperties.isRequireSsl()) http.requiresChannel().anyRequest().requiresSecure();
}
}
So, in case you are using Tomcat behind a proxy, you would have all these properties on your application*.properties file:
security.require-ssl=true
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
Upvotes: 34
Reputation: 1603
For Jetty (tested with 9.2.14), you need to add an extra configuration to the WebAppContext
(adjust the pathSpec
to your taste) :
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
class HttpToHttpsJettyConfiguration extends AbstractConfiguration
{
// http://wiki.eclipse.org/Jetty/Howto/Configure_SSL#Redirecting_http_requests_to_https
@Override
public void configure(WebAppContext context) throws Exception
{
Constraint constraint = new Constraint();
constraint.setDataConstraint(2);
ConstraintMapping constraintMapping = new ConstraintMapping();
constraintMapping.setPathSpec("/*");
constraintMapping.setConstraint(constraint);
ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler();
constraintSecurityHandler.addConstraintMapping(constraintMapping);
context.setSecurityHandler(constraintSecurityHandler);
}
}
Then wire this class by adding an @Configuration
class implementing EmbeddedServletContainerCustomizer
along with a new Connector
that listen to the non secure port :
@Configuration
public class HttpToHttpsJettyCustomizer implements EmbeddedServletContainerCustomizer
{
@Override
public void customize(ConfigurableEmbeddedServletContainer container)
{
JettyEmbeddedServletContainerFactory containerFactory = (JettyEmbeddedServletContainerFactory) container;
//Add a plain HTTP connector and a WebAppContext config to force redirect from http->https
containerFactory.addConfigurations(new HttpToHttpsJettyConfiguration());
containerFactory.addServerCustomizers(server -> {
HttpConfiguration http = new HttpConfiguration();
http.setSecurePort(443);
http.setSecureScheme("https");
ServerConnector connector = new ServerConnector(server);
connector.addConnectionFactory(new HttpConnectionFactory(http));
connector.setPort(80);
server.addConnector(connector);
});
}
}
This implies that the SSL Connector
is already configured and listening on port 443 in this example.
Upvotes: 3