mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +08:00
Add SslInfoContributor and SslHealthIndicator
This commit is contained in:
parent
e722200876
commit
0ef93b2113
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.info;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.actuate.info.BuildInfoContributor;
|
||||
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
|
||||
import org.springframework.boot.actuate.info.GitInfoContributor;
|
||||
|
@ -23,14 +25,18 @@ import org.springframework.boot.actuate.info.InfoContributor;
|
|||
import org.springframework.boot.actuate.info.JavaInfoContributor;
|
||||
import org.springframework.boot.actuate.info.OsInfoContributor;
|
||||
import org.springframework.boot.actuate.info.ProcessInfoContributor;
|
||||
import org.springframework.boot.actuate.info.SslInfoContributor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.boot.info.GitProperties;
|
||||
import org.springframework.boot.info.SslInfo;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
@ -100,4 +106,19 @@ public class InfoContributorAutoConfiguration {
|
|||
return new ProcessInfoContributor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
|
||||
@Order(DEFAULT_ORDER)
|
||||
public SslInfoContributor sslInfoContributor(SslInfo sslInfo) {
|
||||
return new SslInfoContributor(sslInfo);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledInfoContributor(value = "ssl", fallback = InfoContributorFallback.DISABLE)
|
||||
public SslInfo sslInfo(ServerProperties serverProperties, SslBundles sslBundles) {
|
||||
// TODO: Get the certificateValidityThreshold from a property
|
||||
return new SslInfo(serverProperties.getSsl(), sslBundles, Duration.ofDays(7));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.ssl;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
|
||||
import org.springframework.boot.actuate.ssl.SslHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.info.SslInfo;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link SslHealthIndicator}.
|
||||
*
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.4.0
|
||||
*/
|
||||
@AutoConfiguration(before = HealthContributorAutoConfiguration.class)
|
||||
@ConditionalOnEnabledHealthIndicator("ssl")
|
||||
public class SslHealthContributorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "sslHealthIndicator")
|
||||
public SslHealthIndicator sslHealthIndicator(SslInfo sslInfo) {
|
||||
return new SslHealthIndicator(sslInfo);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SslInfo sslInfo(ServerProperties serverProperties, SslBundles sslBundles) {
|
||||
// TODO: Get the certificateValidityThreshold from a property
|
||||
// TODO: This is the same as the one in InfoContributorAutoConfiguration,
|
||||
// should we keep just one?
|
||||
return new SslInfo(serverProperties.getSsl(), sslBundles, Duration.ofDays(7));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for actuator ssl concerns.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.ssl;
|
|
@ -103,6 +103,7 @@ org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagem
|
|||
org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.session.SessionsEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.ssl.SslHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.BraveAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.info;
|
||||
|
||||
import org.springframework.boot.actuate.info.Info.Builder;
|
||||
import org.springframework.boot.info.SslInfo;
|
||||
|
||||
/**
|
||||
* An {@link InfoContributor} that exposes {@link SslInfo}.
|
||||
*
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class SslInfoContributor implements InfoContributor {
|
||||
|
||||
private final SslInfo sslInfo;
|
||||
|
||||
public SslInfoContributor(SslInfo sslInfo) {
|
||||
this.sslInfo = sslInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contribute(Builder builder) {
|
||||
builder.withDetail("ssl", this.sslInfo);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.ssl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health.Builder;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.info.SslInfo;
|
||||
import org.springframework.boot.info.SslInfo.CertificateInfo;
|
||||
import org.springframework.boot.info.SslInfo.CertificateInfo.Validity;
|
||||
|
||||
import static org.springframework.boot.actuate.health.Status.OUT_OF_SERVICE;
|
||||
import static org.springframework.boot.actuate.health.Status.UP;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.EXPIRED;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.NOT_YET_VALID;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.VALID;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.WILL_EXPIRE_SOON;
|
||||
|
||||
/**
|
||||
* {@link HealthIndicator} that checks the certificates the application uses and reports
|
||||
* {@link Status#OUT_OF_SERVICE} when a certificate is invalid or "WILL_EXPIRE_SOON" if it
|
||||
* will expire within the configurable threshold.
|
||||
*
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class SslHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private static final Status WILL_EXPIRE_SOON_STATUS = new Status(WILL_EXPIRE_SOON.name(),
|
||||
"One of the certificates will expire within the defined threshold.");
|
||||
|
||||
private final SslInfo sslInfo;
|
||||
|
||||
public SslHealthIndicator(SslInfo sslInfo) {
|
||||
this.sslInfo = sslInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Builder builder) throws Exception {
|
||||
List<CertificateInfo> notValidCertificates = this.sslInfo.getBundles()
|
||||
.stream()
|
||||
.flatMap(bundle -> bundle.getCertificateChains().stream())
|
||||
.flatMap(certificateChain -> certificateChain.getCertificates().stream())
|
||||
.filter(certificate -> certificate.getValidity().getStatus() != VALID)
|
||||
.toList();
|
||||
|
||||
if (notValidCertificates.isEmpty()) {
|
||||
builder.status(UP);
|
||||
}
|
||||
else {
|
||||
Set<Validity.Status> statuses = notValidCertificates.stream()
|
||||
.map(certificate -> certificate.getValidity().getStatus())
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
if (statuses.contains(EXPIRED) || statuses.contains(NOT_YET_VALID)) {
|
||||
builder.status(OUT_OF_SERVICE);
|
||||
}
|
||||
else if (statuses.contains(WILL_EXPIRE_SOON)) {
|
||||
// TODO: Should we introduce Status.WARNING
|
||||
// (returns 200 but indicates that something is not right)?
|
||||
builder.status(WILL_EXPIRE_SOON_STATUS);
|
||||
}
|
||||
else {
|
||||
builder.status(OUT_OF_SERVICE);
|
||||
}
|
||||
builder.withDetail("certificates", notValidCertificates);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Actuator support for ssl concerns.
|
||||
*/
|
||||
package org.springframework.boot.actuate.ssl;
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.info;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServerSslBundle;
|
||||
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.EXPIRED;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.NOT_YET_VALID;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.VALID;
|
||||
import static org.springframework.boot.info.SslInfo.CertificateInfo.Validity.Status.WILL_EXPIRE_SOON;
|
||||
|
||||
/**
|
||||
* Information about the certificates that the application uses.
|
||||
*
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class SslInfo {
|
||||
|
||||
private final List<Bundle> bundles;
|
||||
|
||||
private final Duration certificateValidityThreshold;
|
||||
|
||||
public SslInfo(Ssl ssl, SslBundles sslBundles, Duration certificateValidityThreshold) {
|
||||
List<Bundle> bundles = new ArrayList<>();
|
||||
for (Entry<String, SslBundle> entry : sslBundles.getBundles().entrySet()) {
|
||||
bundles.add(new Bundle(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
if (ssl.getBundle() == null) {
|
||||
// TODO: WebServerSslBundle.get is called at multiple places
|
||||
// (i.e.: in AbstractConfigurableWebServerFactory#getSslBundle)
|
||||
// so this is a duplicate, can we create one instance and reuse it
|
||||
// (e.g.: a bean) or integrate it with SslBundles
|
||||
// that would make this block unnecessary?
|
||||
bundles.add(new Bundle("webServerSslBundle", WebServerSslBundle.get(ssl, sslBundles)));
|
||||
}
|
||||
this.bundles = Collections.unmodifiableList(bundles);
|
||||
this.certificateValidityThreshold = certificateValidityThreshold;
|
||||
}
|
||||
|
||||
public List<Bundle> getBundles() {
|
||||
return this.bundles;
|
||||
}
|
||||
|
||||
public class Bundle {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final List<CertificateChain> certificateChains;
|
||||
|
||||
private Bundle(String name, SslBundle sslBundle) {
|
||||
this.name = name;
|
||||
this.certificateChains = createCertificateChains(sslBundle.getStores().getKeyStore());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public List<CertificateChain> getCertificateChains() {
|
||||
return this.certificateChains;
|
||||
}
|
||||
|
||||
private List<CertificateChain> createCertificateChains(KeyStore keyStore) {
|
||||
try {
|
||||
return Collections.list(keyStore.aliases())
|
||||
.stream()
|
||||
.map(alias -> new CertificateChain(alias, getCertificates(alias, keyStore)))
|
||||
.toList();
|
||||
}
|
||||
catch (KeyStoreException e) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Certificate> getCertificates(String alias, KeyStore keyStore) {
|
||||
try {
|
||||
return List.of(keyStore.getCertificateChain(alias));
|
||||
}
|
||||
catch (KeyStoreException e) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CertificateChain {
|
||||
|
||||
private final String alias;
|
||||
|
||||
private final List<CertificateInfo> certificates;
|
||||
|
||||
CertificateChain(String alias, List<Certificate> certificates) {
|
||||
this.alias = alias;
|
||||
this.certificates = certificates.stream().map(CertificateInfo::new).toList();
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
public List<CertificateInfo> getCertificates() {
|
||||
return this.certificates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CertificateInfo {
|
||||
|
||||
private final X509Certificate certificate;
|
||||
|
||||
private CertificateInfo(Certificate certificate) {
|
||||
// TODO: Is supporting X509Certificate enough? (I _assume_ yes)
|
||||
if (certificate instanceof X509Certificate x509Certificate) {
|
||||
this.certificate = x509Certificate;
|
||||
}
|
||||
else {
|
||||
this.certificate = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return this.certificate != null ? this.certificate.getSubjectX500Principal().getName() : null;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return this.certificate != null ? this.certificate.getIssuerX500Principal().getName() : null;
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
return this.certificate != null ? this.certificate.getSerialNumber().toString(16) : null;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.certificate != null ? "V" + this.certificate.getVersion() : null;
|
||||
}
|
||||
|
||||
public String getSignatureAlgorithmName() {
|
||||
return this.certificate != null ? this.certificate.getSigAlgName() : null;
|
||||
}
|
||||
|
||||
public Instant getValidityStarts() {
|
||||
return this.certificate != null ? this.certificate.getNotBefore().toInstant() : null;
|
||||
}
|
||||
|
||||
public Instant getValidityEnds() {
|
||||
return this.certificate != null ? this.certificate.getNotAfter().toInstant() : null;
|
||||
}
|
||||
|
||||
public Validity getValidity() {
|
||||
try {
|
||||
if (this.certificate != null) {
|
||||
this.certificate.checkValidity();
|
||||
if (isCloseToBeExpired(this.certificate, SslInfo.this.certificateValidityThreshold)) {
|
||||
return new Validity(WILL_EXPIRE_SOON, "Certificate will expire within threshold (%s) at %s"
|
||||
.formatted(SslInfo.this.certificateValidityThreshold, this.getValidityEnds()));
|
||||
}
|
||||
else {
|
||||
return new Validity(VALID, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (CertificateNotYetValidException exception) {
|
||||
return new Validity(NOT_YET_VALID, "Not valid before %s".formatted(this.getValidityStarts()));
|
||||
}
|
||||
catch (CertificateExpiredException exception) {
|
||||
return new Validity(EXPIRED, "Not valid after %s".formatted(this.getValidityEnds()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCloseToBeExpired(X509Certificate certificate, Duration certificateValidityThreshold) {
|
||||
Instant shouldBeValidAt = Instant.now().plus(certificateValidityThreshold);
|
||||
Instant expiresAt = certificate.getNotAfter().toInstant();
|
||||
return shouldBeValidAt.isAfter(expiresAt);
|
||||
}
|
||||
|
||||
public static class Validity {
|
||||
|
||||
private final Status status;
|
||||
|
||||
private final String message;
|
||||
|
||||
Validity(Status status, String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
|
||||
VALID, NOT_YET_VALID, EXPIRED, WILL_EXPIRE_SOON
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,9 +18,11 @@ package org.springframework.boot.ssl;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
@ -34,6 +36,7 @@ import org.springframework.util.Assert;
|
|||
* @author Scott Frederick
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class DefaultSslBundleRegistry implements SslBundleRegistry, SslBundles {
|
||||
|
@ -67,6 +70,13 @@ public class DefaultSslBundleRegistry implements SslBundleRegistry, SslBundles {
|
|||
return getRegistered(name).getBundle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, SslBundle> getBundles() {
|
||||
return this.registeredBundles.entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toUnmodifiableMap(Entry::getKey, entry -> entry.getValue().getBundle()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBundleUpdateHandler(String name, Consumer<SslBundle> updateHandler) throws NoSuchSslBundleException {
|
||||
getRegistered(name).addUpdateHandler(updateHandler);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.ssl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
|
@ -23,6 +24,7 @@ import java.util.function.Consumer;
|
|||
*
|
||||
* @author Scott Frederick
|
||||
* @author Moritz Halbritter
|
||||
* @author Jonatan Ivanov
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface SslBundles {
|
||||
|
@ -35,6 +37,13 @@ public interface SslBundles {
|
|||
*/
|
||||
SslBundle getBundle(String name) throws NoSuchSslBundleException;
|
||||
|
||||
/**
|
||||
* Return all the {@link SslBundle SslBundles} by name.
|
||||
* @return the bundles
|
||||
* @since 3.4.0
|
||||
*/
|
||||
Map<String, SslBundle> getBundles();
|
||||
|
||||
/**
|
||||
* Add a handler that will be called each time the named bundle is updated.
|
||||
* @param name the bundle name
|
||||
|
|
|
@ -7,6 +7,7 @@ description = "Spring Boot Tomcat SSL smoke test"
|
|||
|
||||
dependencies {
|
||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
|
||||
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
|
||||
|
||||
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
|
||||
testImplementation("org.apache.httpcomponents.client5:httpclient5")
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
server.port = 8443
|
||||
server.ssl.key-store = classpath:sample.jks
|
||||
server.ssl.key-store-password = secret
|
||||
server.ssl.key-password = password
|
||||
server.port=8443
|
||||
#server.ssl.key-store=classpath:sample.jks
|
||||
#server.ssl.key-store-password=secret
|
||||
#server.ssl.key-password=password
|
||||
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoint.health.show-details=always
|
||||
management.info.ssl.enabled=true
|
||||
|
||||
server.ssl.bundle=ssldemo
|
||||
spring.ssl.bundle.jks.ssldemo.keystore.location=classpath:sample.jks
|
||||
spring.ssl.bundle.jks.ssldemo.keystore.password=secret
|
||||
spring.ssl.bundle.jks.ssldemo.keystore.type=JKS
|
||||
spring.ssl.bundle.jks.ssldemo.key.password=password
|
||||
|
|
Loading…
Reference in New Issue
Block a user