How can I add swagger open api 3.0 configuration for secured api's - spring-boot

I've enabled the Swagger open API 3.0 like below:
Added following dependencies
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.2</version>
</dependency>
After that added below bean for customization.
#Bean
public OpenAPI customOpenAPI(#Value("${application-description}") String appDesciption,
#Value("${application-version}") String appVersion) {
return new OpenAPI().info(new Info().title("One Money Backend API").version(appVersion)
.description(appDesciption).termsOfService("http://swagger.io/terms/")
.license(new License().name("Apache 2.0").url("http://springdoc.org")));
}
All is working fine. I'm able to access swagger-ui and api-docs.
But I've few api's which is secured with keycloak OAuth 2.0. When I'm trying to access those secured api's I'm getting unauthorized error (it is expected). I want to test secured api's as well from swagger open api 3.0.
Can some one please help me to enable authorization in swagger open api 3.0 to test my secured api's from swagger.

You need to add SecurityScheme bean as shown below. Based on your authentication type you can change the scheme
for basic
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()
.addSecuritySchemes("basicScheme", new SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("basic")));
}
for JWT
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()
.addSecuritySchemes("bearer-key", new SecurityScheme()
.type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")));
}
More details can be found here

Related

Access token not propagated in Spring Boot 2.7

We use Spring Boot with an OIDC integration to provide authentication and authorization to our users.
Our application acts as Client in the OIDC code flow, calling downstream Resource servers through Http requests to serve user requests.
To have the Client pass the access token of the authenticated user to Resource servers we use the ServletOAuth2AuthorizedClientExchangeFilterFunction and apply that to the org.springframework.web.reactive.function.client.WebClient handling downstream requests.
We recently upgraded from Spring Boot 2.6.7 to 2.7.3 and discovered that the Authentication header containing the access token is no
longer added to outgoing requests, if those requests are scheduled on a thread other than the one serving the original request:
public class MyController {
public Mono<ProductSearchResult> searchByName(SearchProductsQuery query) {
List<String> sortOrder = new ArrayList<>();
System.out.println("--OUTER: " + Thread.currentThread().getName());
return resourceServere.searchByName(query)
.doOnNext(searchResponse -> sortOrder.addAll(searchResponse.getIds()))
.flatMap(searchResponse -> Mono.zip(
getSomething(searchResponse.getIds()),
getSomethingElse(searchResponse.getIds()),
Mono.just(searchResponse.pagination())))
.map(tuple3 -> SearchResultMapper.map(tuple3.getT1(), tuple3.getT2(), tuple3.getT3()));
}
private Mono<List<String>> getSomething(List<String> ids) {
System.out.println("--INNER: " + Thread.currentThread().getName());
if (ids.isEmpty()) {
return Mono.just(new ArrayList<>());
}
return otherResourceServerClient.getStuff(ids);
}
}
Which prints
--OUTER: http-nio-8080-exec-6
--INNER: reactor-http-nio-3
Debugging ServletOAuth2AuthorizedClientExchangeFilterFunction we discovered that the request in the http-nio-thread has the Authentication:
Debugger stopped in ServletOAuth2AuthorizedClientExchangeFilterFunction
whereas the request in the reactor-http thread does not:
Debugger stopped in ServletOAuth2AuthorizedClientExchangeFilterFunction
I can certainly provide more information about our setup, I'm just a bit uncertain what would be relevant.
For starters though, we depend on both spring-starter-webflux and spring-boot-starter-web, as well as on spring-boot-starter-security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Curious to hear if anyone has experienced the same issue?

Centralized Swagger/OpenAPI UI for all the different microservices on a single swagger URL i.e accessing all the URLs through one

I have around 16 microservices built in Spring Boot that communicate with each other and each microservice is having multiple APIs in it. I have configured OpenAPI for each of the microservices. So it gives me 16 "swagger-ui" URLs.
I wonder how can I centralize all Swagger URLs on one single page; I want only 1 swagger URL to access all the 16 microservices.
I have gone through the following as well but didn't get any solution
Centralize Swagger at one place for All microservices
Please guide me to the best way to achieve it.
I found a working solution on GitHub. It works with Spring Cloud Discovery but can be easily adapted to other discovery solutions. The basic idea is to generate for Swagger the list of URLs pointing to the OpenAPI JSON files.
The result is similar to one in the #shadyx's answer, but the list is generated dynamically
#RestController
public class SwaggerUiConfig {
#Autowired
private DiscoveryClient discoveryClient;
#GetMapping("/swagger-config.json")
public Map<String, Object> swaggerConfig() {
List<SwaggerUrl> urls = new LinkedList<>();
discoveryClient.getServices().forEach(serviceName ->
discoveryClient.getInstances(serviceName).forEach(serviceInstance ->
urls.add(new SwaggerUrl(serviceName, serviceInstance.getUri() + "/v3/api-docs"))
)
);
return Map.of("urls", urls);
}
}
application.yaml
springdoc:
swagger-ui:
configUrl: "/swagger-config.json"
Sample of the generated swagger-config.json
{
"urls": [
{
"url": "http://localhost:8088/v3/api-docs/users",
"name": "users"
},
{
"url": "http://localhost:8088/v3/api-docs/stores",
"name": "stores"
}
]
}
If all you µservices use the same host, you can create a Spring Boot µservice which acts as a openAPI gateway using spring-doc. (If they are not using the same host, you will have to deal with CORS issues)
First, add the following dependencies to your new gateway µservice
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Then, add the below configuration
springdoc:
api-docs:
enabled: true
swagger-ui:
configUrl: ${server.servlet.contextPath}/v3/api-docs/swagger-config
url: ${server.servlet.contextPath}/v3/api-docs
urls:
- name: api-customer
url: /customer/v3/api-docs
- name: api-cart
url: /cart/v3/api-docs
- name: api-product
url: /product/v3/api-docs
For each µservice you have to fill in the url property with is the url to the openAPI definition at json or yaml format.
For springdoc library which uses openAPI 3.0 the default url is /${µServiceContextPath}/v3/api-docs
For springfox library which uses openAPI 2.0 the default url is /${µServiceContextPath}/v2/api-docs
Finally access to /${contextPath}/swagger-ui.html and you will be able to select an openAPI

How can I integrate filters defined using javax.ws.rs in to Spring WebClient?

I want to use apache.cxf filter CreateSignatureInterceptor in the spring WebClient but not able to do it.
The interceptor CreateSignatureInterceptor is found in the below dependency
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-security-http-signature</artifactId>
<version>3.4.5</version>
</dependency>
The way I am creating the Spring WebClient is as below
private val webClient = WebClient.builder()
.filter(logRequest())
.clientConnector(JettyClientHttpConnector(httpClient))
.baseUrl(baseUri)
.build()
private fun logRequest(): ExchangeFilterFunction {
return ExchangeFilterFunction.ofRequestProcessor{
log.info(it.url().toString())
// --> how to add the existing apache filters
// CreateSignatureInterceptor()
Mono.just(it)
}
}
In this, we can create or add filters in spring WebClient like logRequest() but I am not sure how to integrate the existing defined filters.
As there are very less sources I find it hard to define the existing filters in the spring webclient.

How to configure Springboot's 2.x actuator in Spring Application(not a SpringBoot Application)? [duplicate]

I've been working on a Spring/Spring MVC application and I'm looking to add performance metrics. I've come across Spring Boot Actuator and it looks like a great solution. However my application is not a Spring Boot application. My application is running in a traditional container Tomcat 8.
I added the following dependencies
// Spring Actuator
compile "org.springframework.boot:spring-boot-starter-actuator:1.2.3.RELEASE"
I created the following config class.
#EnableConfigurationProperties
#Configuration
#EnableAutoConfiguration
#Profile(value = {"dev", "test"})
#Import(EndpointAutoConfiguration.class)
public class SpringActuatorConfig {
}
I even went as far as adding #EnableConfigurationProperties on every configuration class as suggested on another post on StackOverflow. However that didn't do anything. The endpoints are still not being created and return 404s.
First let's clarify that you cannot use Spring Boot Actuator without using Spring Boot.
I was wrong about not being able to it without Spring Boot. See #stefaan-neyts
answer for an example of how to do it.
I created a sample project to show how you could convert a basic SpringMVC application using a minimal amount of Spring Boot auto-configuration.
Original source: http://www.mkyong.com/spring-mvc/gradle-spring-mvc-web-project-example
Converted source: https://github.com/Pytry/minimal-boot-actuator
I could have completely removed the dispatcher-servlet.xml and the web.xml files, but I kept them to show how to perform as minimal a change as possible and to simplify converting more complex projects.
Here is a list of steps I took to convert.
Conversion Process
Add a Java Configuration file annotated with #SpringBootApplication
Add the Application configuration file as a bean to the traditional xml configuration ( added it just after the context scan).
Move view resolvers into Application java configuration.
Alternatively, add the prefix and suffix to application.properties.
You can then inject them with #Value in your application, or delete it entirely and just use the provided spring boot view resolver.
I went with the former.
Removed Default context listener from the spring context xml.
This is important!
Since spring boot will provide one you will get an "Error listener Start" exception if you do not.
Add the spring boot plugin to your build script dependencies (I was using gradle)
Add a mainClassName property to the build file, and set to an empty String (indicates not to create an executable).
Modify dependencies for spring boot actuator
You can use actuator without spring boot.
Add this to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
And then in your config class
#Configuration
#EnableWebMvc
#Import({
EndpointAutoConfiguration.class , PublicMetricsAutoConfiguration.class , HealthIndicatorAutoConfiguration.class
})
public class MyActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
And then you can see the metrics in your application
http://localhost:8085/metrics
Allthough it is not a good idea to use Spring Boot features without Spring Boot, it is possible!
For example, this Java configuration makes Spring Boot Actuator Metrics available without using Spring Boot:
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#Import({ EndpointAutoConfiguration.class, PublicMetricsAutoConfiguration.class })
public class SpringBootActuatorConfig {
#Bean
#Autowired
public EndpointHandlerMapping endpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints);
}
#Bean
#Autowired
public EndpointMvcAdapter metricsEndPoint(MetricsEndpoint delegate) {
return new EndpointMvcAdapter(delegate);
}
}
The Maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
Though the answer is already accepted, I thought of updating my experience. I did not want to convert my application to spring boot using #SpringBootApplication. Refer to another question where I have mentioned the bare minimum code required.
As we already have Spring Boot Actuator 2.x, a recipe to include actuator to an existing Spring MVC project can look like this:
#Configuration
#Import({
EndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
})
#EnableConfigurationProperties(CorsEndpointProperties.class)
class ActuatorConfiguration {
#Bean //taken from WebMvcEndpointManagementContextConfiguration.class
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()));
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> "/";
}
}
I did include
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>2.1.18.RELEASE</version>
</dependency>
for compatibility with the baseline Spring version I've been using (5.1.19.RELEASE)
If your objective is to create an endpoint with metrics for Prometheus a.k.a. OpenMetrics, you can use the Prometheus JVM client which is compatible with Spring framework.
Add dependency:
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>0.16.0</version>
</dependency>
To collect metrics of requests, add as first filter in web-app/WEB-INF/web.xml:
<filter>
<filter-name>prometheusFilter</filter-name>
<filter-class>io.prometheus.client.filter.MetricsFilter</filter-class>
<init-param>
<param-name>metric-name</param-name>
<param-value>webapp_metrics_filter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>prometheusFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
To expose metrics as HTTP endpoint, add servlet:
<servlet>
<servlet-name>prometheus</servlet-name>
<servlet-class>io.prometheus.client.exporter.MetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>prometheus</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>
After that you can see the metrics on the /metrics endpoint.
Time passes, we have Spring 6, SpringBoot 3, JakartaEE as a baseline, but people are still looking to add actuator to legacy spring applications. So a small update: spring + actuator without spring-boot. In fact not much changes (and the changes have already been pointed out).
The dependencies
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.3</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>3.0.1</version>
</dependency>
The actuator configuration
#Configuration
#ImportAutoConfiguration({
EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
HealthContributorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
HeapDumpWebEndpointAutoConfiguration.class,
ThreadDumpEndpointAutoConfiguration.class,
LoggersEndpointAutoConfiguration.class,
PrometheusMetricsExportAutoConfiguration.class,
})
#EnableConfigurationProperties(CorsEndpointProperties.class)
class ActuatorConfiguration {
#Bean //taken from WebMvcEndpointManagementContextConfiguration.class
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath());
return new WebMvcEndpointHandlerMapping(endpointMapping,
webEndpoints,
endpointMediaTypes,
corsProperties.toCorsConfiguration(),
new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath()),
true);
}
#Bean
DispatcherServletPath dispatcherServletPath() {
return () -> WebInitializer.APPLICATION_ROOT;
}
}
The example is easy to run directly from maven jetty plugin (mvn jetty:run-war).
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
</plugin>
you have made the mistake by not introducing the #springboot annotation in your code.When you add #springboot ot will consider as boot program by the compiler automatically and addd the required dependency file for it and your actuator dependency file

Spring Cloud Feign/Ribbon with corporate proxy

I want to consume a REST service from the outside world behind a corporate proxy with authentication.
How do I configure Spring Boot + Spring Cloud Feign/Ribbon to use our proxy?
I believe you're looking for something like this:
import feign.Feign;
import okhttp3.OkHttpClient;
import java.net.InetSocketAddress;
import java.net.Proxy;
...
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy-url", 1234));
OkHttpClient okHttpClient = new OkHttpClient.Builder().proxy(proxy).build();
Feign.builder()
.client(new feign.okhttp.OkHttpClient(okHttpClient))
.target(...);
You just have to additionally add compile 'io.github.openfeign:feign-okhttp:9.5.0' to your project.
The target clause contains your defined Interface. Further reference: https://github.com/OpenFeign/feign
Turns out there is actually a much easier solution.
The following information will be helpful (also for more advanced use cases):
Spring Cloud Commons HTTP Factories
Overriding Feign Defaults
OpenFeign Client can run with several HTTP Clients.
By default it uses java.net.URLConnection, but you can also use ApacheHttpClient or OkHttpClient.
Using Apache Http Client
Here is what you can do to set a proxy using ApacheHttpClient:
Add the following two dependencies to your pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Dependency to switch HttpClient implementation from java.net.URLConnection to Apache HTTP Client -->
<!-- See also: FeignAutoConfiguration for details. -->
<!-- See also: https://cloud.spring.io/spring-cloud-commons/reference/html/#http-clients -->
<!-- See also: https://cloud.spring.io/spring-cloud-openfeign/reference/html/#spring-cloud-feign-overriding-defaults -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
In your app expose the following bean:
// see: https://cloud.spring.io/spring-cloud-commons/reference/html/#http-clients
#Bean
public HttpClientBuilder proxiedHttpClient() {
String proxyHost = "client-envoy";
Integer proxyPort = 80
String proxyScheme = "http";
return HttpClientBuilder.create()
.setProxy(new HttpHost(proxyHost, proxyPort, proxyScheme));
}
That's it - nothing else needs to be configured in application.yaml since ApacheHttpClient will be used by default, if it is on the classpath.
Using Ok Http Client
To set a proxy using OkHttpClient you do a similar thing:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
In your application.yml make sure to enable OkHttpClient and disable ApacheHttpClient:
spring:
cloud:
httpclientfactories:
ok:
enabled: true
apache:
enabled: false
feign:
okhttp:
enabled: true
httpclient:
enabled: false
Instead of HttpClientBuilder expose a bean of type OkHttpClient.Builder.
Spring cloud feign supports three underlying implementations:
Default
Apache HttpClient
OkHttpClient
If using Default:
Create this spring bean (say by defining inside class with #Configuration annotation), no changes required in application properties/yml:
#Bean
public Client feignClient() {
return new Client.Proxied(
null, null, new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
}
If using Apache HttpClient:
that means you have feign.httpclient.enabled: true in application.yml and below in your pom.xml or build.gradle:
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
build.gradle
implementation 'io.github.openfeign:feign-httpclient'
Create this spring bean (say by defining inside class with #Configuration annotation):
#Bean
public CloseableHttpClient feignClient() {
return HttpClientBuilder.create().setProxy(new HttpHost(proxyHost, proxyPort)).build();
}
If using OkHttpClient:
that means you have feign.okhttp.enabled: true in application.yml and below in your pom.xml or build.gradle:
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
build.gradle
implementation 'io.github.openfeign:feign-okhttp'
Create this spring bean (say by defining inside class with #Configuration annotation):
#Bean
public OkHttpClient feignClient() {
return new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
.build();
}

Resources