FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Added example for spring with tomcat · aliyareed/java-tutorials@ff73ef4 · GitHub

Commit ff73ef4

Browse files
committed
Added example for spring with tomcat
1 parent 522a10f commit ff73ef4

14 files changed

Lines changed: 487 additions & 1 deletion

File tree

‎README.MD‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ A repository containing different java tutorials
2424

2525
## Security 🔐
2626
- [Instant Server SSL Reloading with Spring Boot and Jetty](instant-server-ssl-reloading)
27+
- [Instant Server SSL Reloading with Spring Boot and Tomcat](instant-ssl-reloading-with-spring-tomcat)
2728
- [Instant Server SSL Reloading with Vert.x](instant-server-ssl-reloading-with-vertx/vertx-server)
2829
- [Instant Server SSL Reloading with Netty](instant-server-ssl-reloading-with-netty/netty-server)
2930
- [Instant Server SSL Reloading with gRPC](grpc-client-server-with-ssl/instant-server-ssl-reloading-with-grpc)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Instant SSL Reloading 🔐
2+
A server configured with ssl has a key material and trust material. These materials are generated from a keypair and certificate which always have an expiration date.
3+
In a traditional server configuration a reboot of the server is required to apply the latest key material and trust material changes when the keystore and truststore are changed.
4+
A downtime is therefore unavoidable. This project demonstrates with a basic setup how update the server certificate from an external source without the need of restarting your server. In this way you can achieve zero downtime.
5+
6+
The repository contains:
7+
- Server, based on Spring Boot with Tomcat as a server engine
8+
9+
### SSL Updating entrypoint for the server:
10+
The server has two ways to update the existing ssl material:
11+
- File based aka file change listener, see here for the implementation: [FilesBasedSslUpdateService](src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java)
12+
- Databased based, aka database change listener. This option is hosted in a separate module within this repository, see here: [Instant SSL Reloading With Database](https://github.com/Hakky54/java-tutorials/tree/main/instant-ssl-reloading-with-spring-jetty-database)
13+
#### Requirements
14+
- Java 11
15+
- Terminal
16+
17+
#### Start the server
18+
```
19+
mvn spring-boot:run
20+
```
21+
Visit the server with the following url on your browser: https://localhost:8443/api/hello
22+
Open the certificate details in your browser by clicking on the lock logo (on Chrome). You will see a similar certificate detail as shown below:
23+
24+
![alt text](https://github.com/Hakky54/java-tutorials/blob/main/instant-server-ssl-reloading/images/before-reloading.png?raw=true)
25+
26+
Please note down the expiration date. Afterwords you will compare it when you have run the admin application.
27+
28+
#### Refresh the server certificates with the file listener
29+
The file based ssl update service will listen to changes on a specific file on the file system. Adjust the path to your identity and truststore within [FilesBasedSslUpdateService](server/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java).
30+
```java
31+
private static final Path identityPath = Path.of("/path/to/your/identity.jks");
32+
private static final Path trustStorePath = Path.of("/path/to/your/truststore.jks");
33+
```
34+
Also change the passwords if it is different.
35+
```java
36+
private static final char[] identityPassword = "secret".toCharArray();
37+
private static final char[] trustStorePassword = "secret".toCharArray();
38+
```
39+
Adjust the content of the identity and truststore and after 10 seonds the cron job will be triggered to validate if the content has been changed, and it will update the ssl configuration if there are any changes on it.
40+
41+
Refresh your browser tab and open the certificate details again and compare the expiration date with the one you have noted down.
42+
You should have a similar certificate detail as shown below:
43+
44+
![alt text](https://github.com/Hakky54/java-tutorials/blob/main/instant-server-ssl-reloading/images/after-reloading.png?raw=true)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.github.hakky54</groupId>
8+
<artifactId>java-tutorials</artifactId>
9+
<version>1.0.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>instant-ssl-reloading-with-spring-tomcat</artifactId>
13+
<version>1.0.0-SNAPSHOT</version>
14+
<packaging>jar</packaging>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>io.github.hakky54</groupId>
19+
<artifactId>sslcontext-kickstart</artifactId>
20+
<version>${version.sslcontext-kickstart}</version>
21+
</dependency>
22+
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-web</artifactId>
26+
<version>${version.spring}</version>
27+
</dependency>
28+
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-test</artifactId>
32+
<version>${version.spring}</version>
33+
<scope>test</scope>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.junit.jupiter</groupId>
37+
<artifactId>junit-jupiter-api</artifactId>
38+
<version>${version.junit}</version>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.junit.jupiter</groupId>
43+
<artifactId>junit-jupiter-engine</artifactId>
44+
<version>${version.junit}</version>
45+
<scope>test</scope>
46+
</dependency>
47+
</dependencies>
48+
49+
<dependencyManagement>
50+
<dependencies>
51+
<dependency>
52+
<groupId>org.apache.tomcat.embed</groupId>
53+
<artifactId>tomcat-embed-core</artifactId>
54+
<version>${version-tomcat}</version>
55+
</dependency>
56+
</dependencies>
57+
</dependencyManagement>
58+
59+
<build>
60+
<plugins>
61+
<plugin>
62+
<groupId>org.springframework.boot</groupId>
63+
<artifactId>spring-boot-maven-plugin</artifactId>
64+
<version>${version.spring}</version>
65+
<configuration>
66+
<mainClass>nl.altindag.server.App</mainClass>
67+
<finalName>server</finalName>
68+
</configuration>
69+
<executions>
70+
<execution>
71+
<goals>
72+
<goal>repackage</goal>
73+
</goals>
74+
</execution>
75+
</executions>
76+
</plugin>
77+
</plugins>
78+
</build>
79+
80+
</project>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2022 Thunderberry.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package nl.altindag.server;
17+
18+
import org.springframework.boot.SpringApplication;
19+
import org.springframework.boot.autoconfigure.SpringBootApplication;
20+
import org.springframework.scheduling.annotation.EnableScheduling;
21+
22+
@EnableScheduling
23+
@SpringBootApplication
24+
public class App {
25+
26+
public static void main(String[] args) {
27+
SpringApplication.run(App.class, args);
28+
}
29+
30+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2022 Thunderberry.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package nl.altindag.server.config;
17+
18+
import nl.altindag.ssl.SSLFactory;
19+
import org.springframework.beans.factory.annotation.Value;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.Configuration;
22+
23+
@Configuration
24+
public class SSLConfig {
25+
26+
@Bean
27+
public SSLFactory sslFactory(@Value("${ssl.keystore-path}") String keyStorePath,
28+
@Value("${ssl.keystore-password}") char[] keyStorePassword,
29+
@Value("${ssl.truststore-path}") String trustStorePath,
30+
@Value("${ssl.truststore-password}") char[] trustStorePassword,
31+
@Value("${ssl.client-auth}") boolean isClientAuthenticationRequired) {
32+
33+
return SSLFactory.builder()
34+
.withSwappableIdentityMaterial()
35+
.withSwappableTrustMaterial()
36+
.withIdentityMaterial(keyStorePath, keyStorePassword)
37+
.withTrustMaterial(trustStorePath, trustStorePassword)
38+
.withNeedClientAuthentication(isClientAuthenticationRequired)
39+
.build();
40+
}
41+
42+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2022 Thunderberry.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package nl.altindag.server.config;
17+
18+
import nl.altindag.ssl.SSLFactory;
19+
import org.apache.catalina.connector.Connector;
20+
import org.apache.coyote.http11.AbstractHttp11Protocol;
21+
import org.apache.tomcat.util.net.SSLHostConfig;
22+
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
23+
import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
24+
import org.springframework.beans.factory.annotation.Value;
25+
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
26+
import org.springframework.context.annotation.Configuration;
27+
28+
@Configuration
29+
public class SSLConnectorCustomizer implements TomcatConnectorCustomizer {
30+
31+
private final SSLFactory sslFactory;
32+
private final int port;
33+
34+
public SSLConnectorCustomizer(SSLFactory sslFactory, @Value("${server.port}") int port) {
35+
this.sslFactory = sslFactory;
36+
this.port = port;
37+
}
38+
39+
@Override
40+
public void customize(Connector connector) {
41+
connector.setScheme("https");
42+
connector.setSecure(true);
43+
connector.setPort(port);
44+
45+
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) connector.getProtocolHandler();
46+
protocol.setSSLEnabled(true);
47+
48+
SSLHostConfig sslHostConfig = new SSLHostConfig();
49+
SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED);
50+
certificate.setSslContext(new TomcatSSLContext(sslFactory));
51+
sslHostConfig.addCertificate(certificate);
52+
protocol.addSslHostConfig(sslHostConfig);
53+
}
54+
55+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2022 Thunderberry.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package nl.altindag.server.config;
17+
18+
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
19+
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
20+
import org.springframework.context.annotation.Bean;
21+
import org.springframework.context.annotation.Configuration;
22+
23+
@Configuration
24+
public class ServerConfig {
25+
26+
@Bean
27+
public ServletWebServerFactory servletContainer(SSLConnectorCustomizer sslConnectorCustomizer) {
28+
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
29+
tomcat.addConnectorCustomizers(sslConnectorCustomizer);
30+
return tomcat;
31+
}
32+
33+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2022 Thunderberry.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package nl.altindag.server.config;
17+
18+
import nl.altindag.ssl.SSLFactory;
19+
import org.apache.tomcat.util.net.SSLContext;
20+
21+
import javax.net.ssl.*;
22+
import java.security.SecureRandom;
23+
import java.security.cert.X509Certificate;
24+
25+
public final class TomcatSSLContext implements SSLContext {
26+
27+
private final SSLFactory sslFactory;
28+
29+
public TomcatSSLContext(SSLFactory sslFactory) {
30+
this.sslFactory = sslFactory;
31+
}
32+
33+
@Override
34+
public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) {
35+
// not needed to initialize as it is already initialized
36+
}
37+
38+
@Override
39+
public void destroy() {
40+
41+
}
42+
43+
@Override
44+
public SSLSessionContext getServerSessionContext() {
45+
return sslFactory.getSslContext().getServerSessionContext();
46+
}
47+
48+
@Override
49+
public SSLEngine createSSLEngine() {
50+
return sslFactory.getSSLEngine();
51+
}
52+
53+
@Override
54+
public SSLServerSocketFactory getServerSocketFactory() {
55+
return sslFactory.getSslServerSocketFactory();
56+
}
57+
58+
@Override
59+
public SSLParameters getSupportedSSLParameters() {
60+
return sslFactory.getSslParameters();
61+
}
62+
63+
@Override
64+
public X509Certificate[] getCertificateChain(String alias) {
65+
return sslFactory.getKeyManager()
66+
.map(keyManager -> keyManager.getCertificateChain(alias))
67+
.orElseThrow();
68+
}
69+
70+
@Override
71+
public X509Certificate[] getAcceptedIssuers() {
72+
return sslFactory.getTrustedCertificates().toArray(new X509Certificate[0]);
73+
}
74+
75+
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL