How do you configure a self signed certificate programmatically with Spring Boot 3 for Tomcat? - spring-boot

Previous examples of how to configure a self signed certificate with Spring Boot 2.x looked something like this
#Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory server) {
server.addConnectorCustomizers(connector -> {
Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
proto.setSSLEnabled(true);
proto.setKeystoreFile(CERTIFICATE_PATH);
proto.setKeystorePass(CERTIFICATE_PASSWORD);
proto.setKeystoreType(KEYSTORE_TYPE);
proto.setKeyAlias(CERTIFICATE_ALIAS);
});
}
}
Spring Boot 3 moves to Tomcat 10 which removes setKeystoreFile, setKeystorePass, setKeystoreType, and setKeyAlias from the base classes for Http11NioProtocol and I am struggling to find the appropriate way to configure these same parameters in the new environment. I have done my due diligence searching the web but I am struggling to find the replacement method for doing this.

Related

Does Spring Boot Actuator have a Java API?

We customize the Spring Boot Actuator Info endpoint to include the application version number generated during our Jenkins build. We're using gradle to do this:
if (project.hasProperty('BUILD_NUMBER')) {
version = "${BUILD_NUMBER}"
} else {
version = "0.0.1-SNAPSHOT"
}
That works great for adding the version to the /info endpoint, but I'd like to access it when the application starts and print it to the application log.
I'm hoping the values are exposed in some property value (similar to spring.profiles.active) or through a Java API. That way, I could do something like this:
public class MyApplication{
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApplication.class, args);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
System.out.println(environment.getProperty("spring.fancy.path.to.info.version"));
}
}
Looking through the docs, I'm not finding a way to access these values easily in code. Has anyone else had luck with this?
To get exactly the same properties of an actuator endpoint that are exposed through the REST endpoints, you can inject in one of your classes an instance of the respective endpoint class. In your case, the "right" endpoint class would be the InfoEndpoint. There are analogous endpoint classes for metrics, health, etc.
The interface has changed a little between Spring Boot 1.5.x and Spring Boot 2.x. So the exact fully qualified class name or read method name may vary based on the Spring Boot version that you are using. In Boot 1.5.x, you can find most of the endpoints in the org.springframework.boot.actuate.endpoint package.
Roughly, this is how you could build a simple component for reading your version property (assuming that the name of the property inside the info endpoint is simply build.version):
#Component
public class VersionAccessor {
private final InfoEndpoint endpoint;
#Autowired
public VersionAccessor(InfoEndpoint endpoint) {
this.endpoint = endpoint;
}
public String getVersion() {
// Spring Boot 2.x
return String.valueOf(getValueFromMap(endpoint.info()));
// Spring Boot 1.x
return String.valueOf(getValueFromMap(endpoint.invoke()));
}
// the info returned from the endpoint may contain nested maps
// the exact steps for retrieving the right value depends on
// the exact property name(s). Here, we assume that we are
// interested in the build.version property
private Object getValueFromMap(Map<String, Object> info) {
return ((Map<String, Object>) info.get("build")).get("version");
}
}

Togglz don't pick up Spring-Boot configuration from application.yml

I tried to follow the Togglz guide for Spring Boot, so added all necessary dependencies, created a feature enum:
public enum RetrospectiveBoardFeatures implements Feature {
#Label("Name by cookie")
NAME_BY_COOKIE,
#Label("Name by login")
NAME_BY_LOGIN;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
, configured a EnumBasedFeatureProvider to make that enum known to Spring/Togglz:
#SpringBootApplication
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(RetrospectiveBoardFeatures.class);
}
}
This all works fine until I wrote a small unit test to see if the feature toggle configuration is applied to my enum (from application.yml):
togglz:
features:
NAME_BY_COOKIE:
enabled: true
NAME_BY_LOGIN:
enabled: false
Test:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = Application.class)
public class RetrospetiveBoardFeaturesTest {
#Test
public void testCookieFeature() {
assertThat(RetrospetiveBoardFeatures.NAME_BY_COOKIE.isActive(), is(true));
}
}
So my expected result was not met (feature active). Then I added the enabled by default annotation and my feature got active. According to the guide (how I understood it) I don't need to add anything that reads my configuration from Spring and make them known to Togglz. The Togglz samples on GitHub also didn't do anything in this regard (and by looking what Togglz provide in the Spring-Boot starter there is a feature property provider already set up). Maybe I have some wrong versions selected (Spring boot 2.0.1.RELEASE and Togglz 2.5.0.Final)? What did I wrong?
Togglz 2.5.0.Final doesn't support Spring Boot 2 yet. I guess this may be the source of your problem. We are about to release 2.6.0.Final with full Spring Boot 2 support in the next days.
Of course you could give the latest snapshot a try. See all the details here:
https://www.togglz.org/download.html
Also, feel free to join our Gitter chat where we currently discuss all the issue regarding Spring Boot 2 support:
https://gitter.im/togglz/togglz

Instantiating server in Spring Framework 5 and Spring Boot 2

I am using Spring Boot 2 just to try some reactive programming with Spring 5. I created some standard MVC controller.
#RestController
#RequestMapping("/judge/rest")
public class BasicController {
private static final Logger LOGGER = LoggerFactory.getLogger(BasicController.class);
#GetMapping("/hello")
public Mono<String> handle() {
LOGGER.debug("Invoking hello controller");
return Mono.just("Hello WebFlux");
}
}
And standard router function.
#Configuration
public class WebConfig {
#Bean
public RouterFunction<?> helloRoute() {
return route(GET("/judge/router/hello"),
request -> ServerResponse.ok().body(fromPublisher(Mono.just("Hello Router WebFlux"), String.class)));
}
}
My main spring boot application looks like this
#SpringBootApplication
public class JudgeRuleEngineApplication {
public static void main(String[] args) {
SpringApplication.run(JudgeRuleEngineApplication.class, args);
}
}
But in documentation for spring 5 I ran into
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
server.startAndAwait(adapter);
It seems that server is intantiated manually.
My question is when should I instantiate the server like this? Because so far it seems with #SpringBootApplication and main it handles requests just fine.
As the document says
Now there is just one piece of the puzzle missing: running a router
function in an HTTP server. You can convert a router function into a
HttpHandler by using RouterFunctions.toHttpHandler(RouterFunction).
The HttpHandler allows you to run on a wide variety of reactive
runtimes: Reactor Netty, RxNetty, Servlet 3.1+, and Undertow.
Which means the above code which you have shown, uses Reactor Netty as the reactive runtime. If you wish to use any other runtimes which has reactive native adapter, you can do so. In such cases you would instantiate the server like this.
By default Spring boot default to Reactor Netty.

How to configure in Spring Boot 2 (w/ WebFlux) two ports for HTTP and HTTPS?

Can anybody tell me how 2 ports (for HTTP and HTTPS) can be configured when using Spring Boot 2 and WebFlux? Any hint is appreciated!
This isn't directly supported by Spring Boot 2 yet.
But, you may be able to get it to work in a couple of ways.
By default, Spring Boot WebFlux uses Netty. If you are already configured for ssl, then Spring Boot will start up and open port 8443 (or whatever you have configured).
Then, to add 8080, you can do:
#Autowired
HttpHandler httpHandler;
WebServer http;
#PostConstruct
public void start() {
ReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(8080);
this.http = factory.getWebServer(this.httpHandler);
this.http.start();
}
#PreDestroy
public void stop() {
this.http.stop();
}
Which is a bit clunky since your https configuration is in one spot (application.yml) and your http configuration is in Java config, but I have tested this myself with a toy application. Not sure how robust of a solution it is, though.
Another option that may work is to try the equivalent of other suggestions, but use the reactive version of the class, which is TomcatReactiveWebServerFactory. I'm not aware of any way to provide more than one connector for it, but you could possibly override its getWebServer method:
#Bean
TomcatReactiveWebServerFactory twoPorts() {
return new TomcatReactiveWebServerFactory(8443) {
#Override
public WebServer getWebServer(HttpHandler httpHandler) {
// .. copy lines from parent class
// .. and add your own Connector, similar to how tutorials describe for servlet-based support
}
}
}
Also, a bit messy, and I have not tried that approach myself.
Of course, keep track of the ticket so you know when Spring Boot 2 provides official support.
Follow the instructions listed in the link provided by jojo_berlin (here's the link). Instead of using his EmbeddedTomcatConfiguration class though, use this below
#Configuration
public class TomcatConfig {
#Value("${server.http.port}")
private int httpPort;
#Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setPort(httpPort);
factory.addAdditionalTomcatConnectors(connector);
return factory;
}
}
Actually you can define a second connector as described here . So you can define a https connector as your default and an additional HTTP Connector

Spring Boot with Spring Social Google provider

Any example how to integrate Spring Boot application with Spring Social Google (GabiAxel/spring-social-google) provider? I found this project, but it seems to be unfinished. Spring Boot explains how to get it working with Spring Facebook, Twitter, but is it the same for log in with Google?
As you have mentioned in your question, you can use that project hosted on github.
You can use this dependency
In a Configuration class, you will have to extend SocialConfigurerAdapter, override the addConnectionFactories method and add GoogleConnectionFactory. For example :
#Configuration
#EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.app-id"), environment.getProperty("spring.social.google.app-secret"));
googleConnectionFactory.setScope("https://www.googleapis.com/auth/plus.login");
connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
}
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
}
You can use this along with the Spring Social examples.

Resources