diff --git a/README.md b/README.md index 7d87555..4d2aa7b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # spring-samples Samples of spring or spring boot + + + +## Spring-boot-tls + +该模块下的主要功能包括:Spring Boot配置证书提供HTTPS服务,Spring Boot监听多个端口,Spring Boot每个端口处理的资源支持配置。 diff --git a/pom.xml b/pom.xml index f482c91..358c43c 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ spring-cloud-gateway-nacos spring-boot-jwt jpa-mysql + spring-boot-tls diff --git a/spring-boot-tls/pom.xml b/spring-boot-tls/pom.xml new file mode 100644 index 0000000..fefb041 --- /dev/null +++ b/spring-boot-tls/pom.xml @@ -0,0 +1,30 @@ + + + + spring-samples + io.ctlove0523.spring + 0.0.1-SNAPSHO + + 4.0.0 + + spring-boot-tls + + + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.yaml + snakeyaml + + + + \ No newline at end of file diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/AllowedFilter.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/AllowedFilter.java new file mode 100644 index 0000000..5c12247 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/AllowedFilter.java @@ -0,0 +1,67 @@ +package io.github.ctlove0523.tls; + +import org.springframework.util.AntPathMatcher; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class AllowedFilter implements Filter { + private final Map> patternMap = new HashMap<>(); + private final AntPathMatcher antPathMatcher; + + public AllowedFilter(List urls) { + this.antPathMatcher = new AntPathMatcher(); + this.antPathMatcher.setCachePatterns(true); + this.antPathMatcher.setCaseSensitive(true); + + if (Objects.isNull(urls)) { + urls = new ArrayList<>(); + } + + for (ServerAllowedUrl url : urls) { + int port = url.getPort(); + if (url.getUrls() == null) { + patternMap.put(port, new ArrayList<>()); + } else { + patternMap.put(port, url.getUrls()); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + int serverPort = request.getServerPort(); + + if (!patternMap.containsKey(serverPort)) { + chain.doFilter(httpRequest, httpResponse); + return; + } + + List patterns = patternMap.get(serverPort); + for (String pattern : patterns) { + String uri = httpRequest.getRequestURI(); + if (antPathMatcher.match(pattern, uri)) { + chain.doFilter(httpRequest, httpResponse); + return; + } + + } + + httpResponse.setStatus(401); + + } +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigure.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigure.java new file mode 100644 index 0000000..c849796 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigure.java @@ -0,0 +1,69 @@ +package io.github.ctlove0523.tls; + +import java.util.List; + +public class ConnectorConfigure { + private int port; + private String scheme; + private boolean sslEnabled; + private String keystoreFile; + private String KeystorePass; + private String keyAlias; + private List allowedUrls; + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public boolean isSslEnabled() { + return sslEnabled; + } + + public void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + public String getKeystoreFile() { + return keystoreFile; + } + + public void setKeystoreFile(String keystoreFile) { + this.keystoreFile = keystoreFile; + } + + public String getKeystorePass() { + return KeystorePass; + } + + public void setKeystorePass(String keystorePass) { + KeystorePass = keystorePass; + } + + public String getKeyAlias() { + return keyAlias; + } + + public void setKeyAlias(String keyAlias) { + this.keyAlias = keyAlias; + } + + public List getAllowedUrls() { + return allowedUrls; + } + + public void setAllowedUrls(List allowedUrls) { + this.allowedUrls = allowedUrls; + } +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigureRepository.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigureRepository.java new file mode 100644 index 0000000..2784331 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ConnectorConfigureRepository.java @@ -0,0 +1,8 @@ +package io.github.ctlove0523.tls; + +import java.util.List; + +public interface ConnectorConfigureRepository { + + List load(); +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ServerAllowedUrl.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ServerAllowedUrl.java new file mode 100644 index 0000000..ad7ea8c --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/ServerAllowedUrl.java @@ -0,0 +1,24 @@ +package io.github.ctlove0523.tls; + +import java.util.List; + +public class ServerAllowedUrl { + private int port; + private List urls; + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public List getUrls() { + return urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TestController.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TestController.java new file mode 100644 index 0000000..ad11950 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TestController.java @@ -0,0 +1,82 @@ +package io.github.ctlove0523.tls; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.UUID; + +@Controller +public class TestController { + public static void main(String[] args) { + AntPathMatcher antPathMatcher = new AntPathMatcher(); + boolean result= antPathMatcher.match("/**", "/api/hello"); + + System.out.println(result); + } + + @RequestMapping(value = "/api/apps/{appId}", method = RequestMethod.GET) + public ResponseEntity showApp(@PathVariable(name = "appId") String appId) { + App app = new App(); + app.setId(appId); + app.setName("hello app"); + return ResponseEntity.ok(app); + } + + @RequestMapping(value = "/api/health", method = RequestMethod.GET) + public ResponseEntity healthCheck() { + return ResponseEntity.ok("health"); + } + + @RequestMapping(value = "/api/users", method = RequestMethod.GET) + public ResponseEntity showUser() { + User user = new User(); + user.setId(UUID.randomUUID().toString()); + user.setName("hello app"); + return ResponseEntity.ok(user); + } +} + +class User { + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +class App { + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TlsServer.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TlsServer.java new file mode 100644 index 0000000..4a7e0c4 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/TlsServer.java @@ -0,0 +1,13 @@ +package io.github.ctlove0523.tls; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@EnableAutoConfiguration +public class TlsServer { + public static void main(String[] args) { + SpringApplication.run(TlsServer.class, args); + } +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/YamlConnectorConfigureRepository.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/YamlConnectorConfigureRepository.java new file mode 100644 index 0000000..31b5db6 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/YamlConnectorConfigureRepository.java @@ -0,0 +1,67 @@ +package io.github.ctlove0523.tls; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Component +public class YamlConnectorConfigureRepository implements ConnectorConfigureRepository { + + @Autowired + private ResourceLoader resourceLoader; + + @SuppressWarnings("unchecked") + public List load() { + List connectorConfigures = new ArrayList<>(); + Yaml yaml = new Yaml(); + Resource resource = resourceLoader.getResource("classpath:application.yaml"); + try { + Map applicationConfig = yaml.load(resource.getInputStream()); + List> configs = (List>) applicationConfig.get("servers"); + for (Map config : configs) { + ConnectorConfigure connectorConfigure = createConnectorConfigure(config); + connectorConfigures.add(connectorConfigure); + } + + return connectorConfigures; + } catch (IOException e) { + e.printStackTrace(); + } + + return new ArrayList<>(); + } + + private ConnectorConfigure createConnectorConfigure(Map config) { + ConnectorConfigure connectorConfigure = new ConnectorConfigure(); + connectorConfigure.setScheme((String) config.get("scheme")); + connectorConfigure.setPort((int) config.get("port")); + + if (Objects.nonNull(config.get("sslEnabled"))) { + connectorConfigure.setSslEnabled((boolean) config.get("sslEnabled")); + } + if (Objects.nonNull(config.get("keystoreFile"))) { + connectorConfigure.setKeystoreFile((String) config.get("keystoreFile")); + } + if (Objects.nonNull(config.get("keystorePass"))) { + connectorConfigure.setKeystorePass(config.get("keystorePass").toString()); + } + + if (Objects.nonNull(config.get("keyAlias"))) { + connectorConfigure.setKeyAlias((String) config.get("keyAlias")); + } + + if (Objects.nonNull(config.get("allowedUrls"))) { + connectorConfigure.setAllowedUrls((List) config.get("allowedUrls")); + } + + return connectorConfigure; + } +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/FilterConfigure.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/FilterConfigure.java new file mode 100644 index 0000000..1f02c40 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/FilterConfigure.java @@ -0,0 +1,42 @@ +package io.github.ctlove0523.tls.config; + +import io.github.ctlove0523.tls.AllowedFilter; +import io.github.ctlove0523.tls.ConnectorConfigureRepository; +import io.github.ctlove0523.tls.ServerAllowedUrl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.ServletContext; +import java.util.List; +import java.util.stream.Collectors; + +@Configuration +public class FilterConfigure { + + @Autowired + private ServletContext servletContext; + + @Autowired + private ConnectorConfigureRepository repository; + + @Bean + public FilterRegistrationBean allowedFilter() { + List allowedUrls = repository.load() + .stream() + .map(connectorConfigure -> { + ServerAllowedUrl serverAllowedUrl = new ServerAllowedUrl(); + serverAllowedUrl.setPort(connectorConfigure.getPort()); + serverAllowedUrl.setUrls(connectorConfigure.getAllowedUrls()); + return serverAllowedUrl; + }).collect(Collectors.toList()); + AllowedFilter allowedFilter = new AllowedFilter(allowedUrls); + FilterRegistrationBean filter = new FilterRegistrationBean<>(); + filter.setFilter(allowedFilter); + filter.setOrder(0); + + return filter; + } + +} diff --git a/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/TlsServerConfigure.java b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/TlsServerConfigure.java new file mode 100644 index 0000000..6adcc90 --- /dev/null +++ b/spring-boot-tls/src/main/java/io/github/ctlove0523/tls/config/TlsServerConfigure.java @@ -0,0 +1,92 @@ +package io.github.ctlove0523.tls.config; + +import io.github.ctlove0523.tls.ConnectorConfigure; +import io.github.ctlove0523.tls.ConnectorConfigureRepository; +import org.apache.catalina.connector.Connector; +import org.apache.coyote.http11.Http11NioProtocol; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; + +@Component +public class TlsServerConfigure implements WebServerFactoryCustomizer { + + @Autowired + private ConnectorConfigureRepository configureRepository; + + @Override + public void customize(TomcatServletWebServerFactory factory) { + Connector[] connectors = createConnectors(); + TomcatConnectorCustomizer customizer = new TomcatConnectorCustomizer() { + @Override + public void customize(Connector connector) { + Connector copy = connectors[0]; + connector.setPort(copy.getPort()); + connector.setScheme(copy.getScheme()); + + Http11NioProtocol handler = (Http11NioProtocol)connector.getProtocolHandler(); + Http11NioProtocol copyHandler = (Http11NioProtocol)copy.getProtocolHandler(); + handler.setSSLEnabled(copyHandler.isSSLEnabled()); + handler.setKeystoreFile(copyHandler.getKeystoreFile()); + handler.setKeystorePass(copyHandler.getKeystorePass()); + handler.setKeyAlias(copyHandler.getKeyAlias()); + } + }; + factory.addConnectorCustomizers(customizer); + // used for health check + for (int i = 1; i < connectors.length; i++) { + factory.addAdditionalTomcatConnectors(connectors[i]); + } + } + + private Connector[] createConnectors() { + List configures = configureRepository.load(); + if (configures.isEmpty()) { + return new Connector[0]; + } + + Connector[] connectors = new Connector[configures.size()]; + for (int i = 0; i < configures.size(); i++) { + Connector connector = new Connector(); + connector.setPort(configures.get(i).getPort()); + connector.setScheme(configures.get(i).getScheme()); + if (configures.get(i).isSslEnabled()) { + Http11NioProtocol handler = (Http11NioProtocol) connector.getProtocolHandler(); + handler.setSSLEnabled(true); + handler.setKeystoreFile(configures.get(i).getKeystoreFile()); + handler.setKeystorePass(configures.get(i).getKeystorePass()); + handler.setKeyAlias(configures.get(i).getKeyAlias()); + } + connectors[i] = connector; + } + + return connectors; + } + + private Connector createHttpConnector() { + Connector connector = new Connector(); + connector.setPort(8080); + connector.setScheme("http"); + return connector; + } + + private InetAddress getBindHost() { + try { + return Inet4Address.getByName("localhost"); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + return null; + } + + public String getKeystoreFile() { + return "D:\\codes\\GitHub\\spring-samples\\spring-boot-tls\\src\\main\\resources\\keystore.p12"; + } +} diff --git a/spring-boot-tls/src/main/resources/application.yaml b/spring-boot-tls/src/main/resources/application.yaml new file mode 100644 index 0000000..5e4d997 --- /dev/null +++ b/spring-boot-tls/src/main/resources/application.yaml @@ -0,0 +1,13 @@ +servers: + - port: 8090 + scheme: https + sslEnabled: true + keystoreFile: D:\codes\GitHub\spring-samples\spring-boot-tls\src\main\resources\keystore.p12 + keystorePass: 123456 + keyAlias: tls-server + allowedUrls: + - /** + - port: 8080 + scheme: http + allowedUrls: + - /api/health diff --git a/spring-boot-tls/src/main/resources/keystore.p12 b/spring-boot-tls/src/main/resources/keystore.p12 new file mode 100644 index 0000000..fa52840 Binary files /dev/null and b/spring-boot-tls/src/main/resources/keystore.p12 differ