Swagger Base URL is wrong for my application deployed on AWS - swagger

I have a Spring Boot application deployed and configured as AWS Route 53 > AWS Load Balance -> 2 EC2 instances which hosted the Spring Boot application.
The URL for the Swagger is
https://applicationXYZ.company.net/release/swagger-ui.html
I'm able to see the page without any issue. But we can't use the 'Tryout' feature because the Base URL is wrong.
On top of the page I do see information as
[ Base URL: service/release]
I have no idea where 'service' became my base URL. I also hit api-docs and also see 'server' in 'host' field.
Could you please help on this?
Note: I'm using Spring Boot Starter 2.0.8.RELEASE and Swagger 2.9.2 (without any Spring Security)
Thanks,

Did you ever try to make a redirect,
//Do redirection inside controller
#RequestMapping("/swagger")
public String greeting() {
return "redirect:/swagger-ui.html";
}
you can try to add bean too, inside main method,
#Bean
RouterFunction<ServerResponse> routerFunction() {
return route(GET("/swagger"), req ->
ServerResponse.temporaryRedirect(URI.create("swagger-ui.html")).build());
}
refer: How to change Swagger-ui URL prefix?

Related

Swagger UI : Failed to Load API Definition

I've deployed a simple Web API in net5 with swagger enabled (using default settings), which means in Startup.cs:
Configure Services method:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyCompany.WebApi", Version = "v1" });
});
Configure method:
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyCompany.WebApi v1"));
And i deploy the same application to 2 Local IIS websites, the first as an application of the Default WebSite running on default port 80, as shown below:
And the second as a Separate WebSite node running on port 8085, as shown below:
Then for the second (hosted as a separate WebSite node), all works fine, so i can see my API definition:
But for the first, hosted as an application under the Default Web Site the API documentation can not be loaded:
Even though the swagger.json file is accessible:
So it look's like swagger is searching for the index.html to display the Swagger-UI in the "root" of the WebSite, and in the case of the first option where the application is hosted under the Default WebSite folder it can not find a way to display the swagger UI. Do we need to add something specific in the swagger definition in this case ?
Thx for any responses !
Emmanuel.
Did you tried this:
app.UseSwaggerUI(c => c.SwaggerEndpoint("swagger/v1/swagger.json", "MyCompany.WebApi v1"));
i removed beginning '/' from swagger json path.

Spring SAML redirect URL after login which contains # character

I am using Spring SAML for my application and I faced a problem with redirect URL after logged in successfully.
I am trying to save the URL before login to the app.
For example, When I access the link as http://localhost:8080/myapp/#request/123/details, spring-security will redirect to the login page.
I expected that after logged in successfully, the app auto redirect to the above URL. I have configured sucessRedirectHandler by using SavedRequestAwareAuthenticationSuccessHandler.
But, after logging in, the application redirects to https://localhost:8080/myapp/.
I also debugged and saw that the request URL does not contains "#request/123/details" part.
Do you have any ideas for this case?
Thank you.
Have you tried setting SAMLEntryPoint options, which preserves the requested URL as relay state.Check below code
#Bean
public SAMLEntryPoint samlEntryPoint() {
SAMLEntryPoint entryPoint = new SAMLEntryPoint();
entryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
return entryPoint;
}
#Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
WebSSOProfileOptions options = new WebSSOProfileOptions();
options .setBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
options.setRelayState(current-SP-URL);
return options;
}

log4j2 monitorInterval using spring cloud config server

I'm trying to read the log4j2 configuration from config server during application startup.
bootstrap.yml
spring:
application:
name: loggingApp
cloud:
config:
uri: http://localhost:8888
logging:
config: http://localhost:8888/loggingApp/raw/master/loggingApp-log4j2-DEV.xml
The application seems to get the configuration properly during start up as I see the appropriate log levels. However, the automatic configuration doesn't seem to work. When I change the log level of the loggers, looks like it didn't read the updated config from config server after the monitorInterval has passed. I've set the monitorInterval to 10 seconds. As per the documentation the minimum interval should be 5 seconds. If I point to a file on local drive, instead of the config server url, it is working fine. I tried using -Dlog4j.configurationFile as jvm arg as well as spring configuration logging.config to see if one of those work, but none worked.
https://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticReconfiguration
https://logging.apache.org/log4j/2.x/log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html
I'm using spring-boot 2.2.5.RELEASE, log4j2 2.13.1 and spring-cloud Hoxton.SR3 versions.
This is how the git repo looks like where config files are being read from
I too tried the same and it did not work. It seems the <Configuration monitorInternal="10"> property would only have worked if we use log4j.configurationFile property to load the log4j2 config file as mentioned in Apache's Log4j Spring Cloud Configuration Guide .
I used the spring actuator refresh approach to accomplish this task. However this does not reload the configs on its own. So there is work around to meet the required ends. Create a scheduler in your spring application that hits the actuator refresh url on specified intervals as below
#Component
#EnableScheduling
public class RefreshScheduler {
#Autowired
private RestTemplate restTemplate;
#Scheduled(fixedRate = 60000)
private void postRefreshEndPoint() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(headers);
restTemplate.postForEntity("http://localhost:8080/actuator/refresh", entity, String.class);
}
}
This is surely a very dirty way of somehow achieving the required functionality. It works though :)
As a side note : Isn't it better to not have automatic refresh? My argument would be : It's possible developers by mistake ( or just for fun ) do changes to the configs in GIT. And this will lead to a change in the live application in prodcution.
However if we have the actuator refresh URL as mandatory for refreshing the spring context, this can be stopped. Only authorized memebers from the production/operations team would have access to this refresh url and they can decide whether to actually refresh the production spring beans when a commit on GIT has been done.

Changing the Order of the Spring Security WebFilter

Changing the Order of the Spring Security WebFilter
I have an API Gateway implemented using Spring Cloud Gateway that uses Spring Security. Spring Security for WebFlux is implemented as a WebFilter right at the beginning of the filter chain. So after successful authentication the request would be forwarded to Spring Cloud Gateway's RoutePredicateHandlerMapping, which would try to deduce the destination based on the URL pattern, and then it would go to a FilteringWebHandler to execute the other filters of Spring Cloud Gateway.
My problem is the following: I have implemented a customized authentication algorithm which uses query string and header variables as credentials for authentication according to the requirements of the project, an this is working without any problem. The problem occurred when we needed to add a small customization for the authentication algorithm that is path independent. When the request reaches the WebFilter of Spring Security, pattern matching is not yet done so I do not know which application does it point to, for example:
app1:
-Path: /app1/**
app2:
-Path: /app2/**
Which means that instead of having authentication -> route mapping -> filtering web handler I should do route mapping -> authentication -> filtering web handler. Not that these three components are not similar, one of them is a filter another is a mapper and the last one is web handler. Now I know how to customize them but the problem is that I do not know how to intercept the Netty server building process in order to change the order of these operations. I need to wait for the building process to end and alter the content of the server before it starts. How can I do that?
EDIT: here is the final solution:
So here is how I did it:
Goal: removing the WebFilter of Spring Security from the default HttpHandler, and inserting it between RoutePredicateRouteMapping and the FilteringWebHandler of Spring Cloud Gateway
Why: Because I need to know the Application ID while carrying on my customized authentication process. This Application ID is attached to the request by the RoutePredicateRouteMapping by matching the request's URL to a predefined list.
How did I do it:
1- Removing the WebFilter of Spring Security
I created an HttpHandler bean that invokes the default WebHttpHandlerBuilder and then customize the filters. As a bonus, I removed unneeded filters in order to increase the performance of my API Gateway
#Bean
public HttpHandler httpHandler() {
WebHttpHandlerBuilder webHttpHandlerBuilder = WebHttpHandlerBuilder.applicationContext(this.applicationContext);
MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter = this.applicationContext.getBean(MY_AUTHENTICATED_HANDLER_BEAN_NAME, MyAuthenticationHandlerAdapter.class);
webHttpHandlerBuilder
.filters(filters ->
myAuthenticationHandlerAdapter.setSecurityFilter(
Collections.singletonList(filters.stream().filter(f -> f instanceof WebFilterChainProxy).map(f -> (WebFilterChainProxy) f).findFirst().orElse(null))
)
);
return webHttpHandlerBuilder.filters(filters -> filters
.removeIf(f -> f instanceof WebFilterChainProxy || f instanceof WeightCalculatorWebFilter || f instanceof OrderedHiddenHttpMethodFilter))
.build();
}
2- Wrapping Spring Cloud Gateway's FilteringWebHandler with Spring Web's FilteringWebHandler with the added WebFilter
I created my own HandlerAdapter which would match against Spring Cloud Gateway's FilteringWebHandler and wrap it with Spring Web's FilteringWebHandler plus the security filter I extracted in the first step
#Bean
public MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter() {
return new MyAuthenticationHandlerAdapter();
}
public class MyAuthenticationHandlerAdapter implements HandlerAdapter {
#Setter
private List<WebFilter> securityFilter = new ArrayList<>();
#Override
public boolean supports(Object handler) {
return handler instanceof FilteringWebHandler;
}
#Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
org.springframework.web.server.handler.FilteringWebHandler filteringWebHandler = new org.springframework.web.server.handler.FilteringWebHandler((WebHandler) handler, securityFilter);
Mono<Void> mono = filteringWebHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
This way I could achieve better performance with highly customized HttpHandler pipeline that I suppose to be future-proof
END EDIT
Spring Security for WebFlux is implemented as a WebFilter which is executed almost as soon as a request is received. I have implemented custom authentication converter and authentication manager which would extract some variables from the header and URL and use them for authentication. This is working without any problem.
Now I needed to add another variable taken from RoutePredicateRouteMapping before authentication is done. What I want exactly is to remove the WebFilter (called WebFilterChainProxy) from its current position and put it between the RoutePredicateRouteMapping and the FilteringWeHandler.
Here is how the default process goes:
ChannelOperations calls ReactorHttpHandlerAdapter which calls HttpWebHandlerAdapter, ExceptionHandlingWebHandler, and then org.springframework.web.server.handler.FilterWebHandler.
This WebHandler would invoke its filters and then call the DispatchHandler. One of those filters is the WebFilterChainProxy that does the authentication for Spring Security. So first step is removing the filter from here.
Now the DispatchHandler which is called after the filters would invoke RoutePredicateHandlerMapping, which would analyze the routes and give me the route ID that I need, and then it would call the org.springframework.cloud.gateway.handler.FilteringHandler (this is not the same FilteringHandler above), and that in turn would call the other filters of the Spring Cloud Gateway. What I want here is to invoke the filter after RoutePredicatehandlerMapping and before org.springframework.cloud.gateway.handler.FilteringHandler.
What I ended doing was the following:
I created and WebHttpHandlerBuilder that would remove WebFilterChainProxy and pass it as a parameter to a customized DispatcherHandler. Now that the filter is removed the request would pass the first layers without requiring authentication. In my customized DispatcherHandler I would invoke the RoutePredicateHandlerMapping and then pass the exchange variable to the WebFilterChainProxy to do the authentication before passing it to the org.springframework.cloud.gateway.handler.FilteringHandler, which worked perfectly!
I still think that I'm over engineering it and I hope that there is a way to do it using annotations and configuration beans instead of all these customized classes (WebHttpHandlerBuilder and DispatcherHandler).
You should probably implement that security filter as a proper GatewayFilter, since only those are aware of the other GatewayFilter instances and can be ordered accordingly. In your case, you probably want to order it after the routing one.
Also, please don't cross-post, the Spring team is actively monitoring StackOverflow.
I had a similar problem. The accepted solution, while interesting, was a bit drastic for me. I was able to make it work simply by adding my custom filter before SecurityWebFiltersOrder.AUTHENTICATION in the security configuration. This is similar to what I've done with success in a regular Spring mvc application.
Here's an example using oauth authentication. tokenIntrospector is my custom introspector, and requestInitializationFilter is the filter that grabs the tenant id and stashes it in the context.
#AllArgsConstructor
#Configuration
#EnableWebFluxSecurity
public class WebApiGatewaySecurityConfiguration {
private final GatewayTokenIntrospector tokenIntrospector;
private final GatewayRequestInitializationFilter requestInitializationFilter;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// #formatter:off
http
.formLogin().disable()
.csrf().disable()
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.opaqueToken(c -> c.introspector(tokenIntrospector)))
.addFilterBefore(requestInitializationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
// #formatter:on
}
}

Springfox Swagger UI behind reverse proxy

I have configured a Spring Boot application with Swagger API documentation and configured Swagger UI.
I also run my backend application behind a reverse proxy that maps all requests from host:port/api to backend_host:port/, when running locally on localhost I map localhost:8082/api. In production a similar mapping is applied.
When I open the Swagger UI from localhost:8082/api/swagger-ui.html it shows the following lines below the title:
[ Base URL: localhost:8080 ]
http://localhost:8082/api/v2/api-docs
When I invoke any rest operation swagger always tries to perform it against localhost:8080 which then fails due to the same origin policy.
I am aware of using pathProvider but it only affects the base URL's path part, not the domain and port. So I can only use it to change the base URL to localhost:8080/api but I would need it to change to localhost:8082/api. Is there a way to set the host dynamically to the current host and port that is active in the browser?
.pathProvider (new RelativePathProvider (servletContext) {
#Override
public String getApplicationBasePath() {
return "/api";
}
})
In my case with a spring-boot:2.2.6 application with springdoc-openapi-ui:1.3.0 (that also has embedded the swagger UI), I solved the proxy problem setting the server URL in this way:
#Configuration
public class OpenApiConfig {
#Value("${server}")
private String url;
#Bean
#Profile("prod")
public OpenAPI customConfiguration() {
return new OpenAPI()
.servers(Collections
.singletonList(new Server().url(url))) //real public URL
.components(new Components())
.info(new Info().title("Dummy API Docs")
.description("Dummy REST API documentation"));
}
}
This change is reflected in the contract (https://real-domain.com/api-docs):
And in the Swagger UI (https://real-domain.com/swagger-ui/index.html?configUrl=/api-docs/swagger-config)
I think in your case you need to configure your proxy to set HTTP Header
(which will be forwarded to your target backend)
to "notify" Swagger endpoints to return custom URL in /apidocs endpoint.
Please configure proxy to set header X-Forwarded-Host to value from Host request header
Explanation:
In your browser when you will visit a url eg. https://my.domain.com/api/swagger-ui.html
the proxy should create and forward header X-Forwarded-Host: my.endpoint.com
to your backend localhost:8082/api/swagger-ui.html
-> so the Swagger /apidocs enpoint could take this header into consideration in response JSON.
My own case - in Microsoft IIS:
I needed to configure Microsoft IIS to serve Swagger IU from Apache Tomcat on 8080 port on HTTPS domain,
so I needed to have following configuration:
<serverVariables>
<set name="HTTP_X_FORWARDED_HOST" value=“{HTTP_HOST}” />
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
</serverVariables>
JuanMorenos answer helped me, however, if anyone is using Springboot and annotations with OpenAPI you can define the URL in your main class
#SpringBootApplication
#OpenAPIDefinition(info = #Info(
version = "2.0",
title = "Swagger - My application",
description = "A description of the application"),
servers = #Server(
url = "http://yourhost:yourport",
description = "A description of the Server "
))

Resources