springboot swagger3 "Failed to load remote configuration." - spring-boot

Spring Boot 2.6.3 with Springdoc.
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.5</version>
</dependency>
In applicaton.yaml, when I set the path as /v3/api-docs or remove it, that means use the default path "/v3/api-docs".
The Swagger UI page shows up correctly with the APIs
http://localhost:8080/swagger-ui/index.html
But I want to overite the path as below
api-docs.path: /bus/v3/api-docs
then Swagger UI displays the "Failed to load remote configuration" error:

Make sure to add "/v3/api-docs/**" in configure method.
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui/**", "
/v3/api-docs/**");
}
}

If you are using Spring Security in your app, you must include the URL in the configs.
Add the code below please to your project.
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui/**", "/bus/v3/api-docs/**");
}
}

I had the same problem, If you are behind a reverse proxy, the fix was to add the following property in application.yml
server:
forward-headers-strategy: framework
this is needed due to the following
Swagger relies on internal routing to make requests from the clients perspective. Putting the service behind a reverse-proxy without providing the X-Forwarded headers will result in the user not being able to use the documentation as intended
source -> https://medium.com/swlh/swagger-spring-boot-2-with-a-reverse-proxy-in-docker-8a8795aa3da4

Perform "Empty cache and hard refresh" in your browser.

I think I have solved the problem (thanks to #Ivan Zaitsev), just wanted to add more clarification to the answer.
I too have changed the api-docs.path property and I had the same problem. When I inspect the requests on swagger UI page, swagger-config request returns 404 since it was still trying to get the config from the old URL.
Even though I have changed api-docs.path property, here is the request URL that tries to retrieve swagger-config.
http://localhost:8080/api/v3/api-docs/swagger-config
It turned out to be a problem related to openapi-ui, because I was able to solve it when I cleared the browser cache and cookies. It is better do to the tests with incognito browser since it does not hold any data on the session.

If you are using SpringBoot v3, you must use springdoc-openapi v2:
https://springdoc.org/v2/
With gradle, for example:
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'

Related

Unable to Remove ;jsessionid in a Spring Boot / Web Flow Application's URL when Deployed to Tomcat 8.5

I'm working on a Java application where a user registers a password for his/her account. The following are being used:
Spring Boot
Spring MVC
Spring Web Flow
Spring Security
Thymeleaf
Interceptor (for checking the session in the preHandle method)
For the Spring Security part, there's really no authentication required. I just use it to handle CSRF and the configuration is as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF feature only
http.authorizeRequests().anyRequest().permitAll();
}
}
Now, this is where things get messy. When I deploy it to Tomcat in a Unix environment, ;jsessionid gets appended to the URL and Spring Security is not happy. I have scrounged the Internet and found the following solutions to remove it (alongside my results).
server.servlet.session.tracking-modes=cookie in application.properties does nothing.
web.xml
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
or
#Configuration
public class WebConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
HashSet<SessionTrackingMode> set = new HashSet<>();
set.add(SessionTrackingMode.COOKIE);
servletContext.setSessionTrackingModes(set);
}
}
yields an IllegalArgumentException: The session tracking mode [COOKIE] requested for context [/<context-name>] is not supported by that context
I'm about to pull what remains of my hair off so I reverted any cookie-related changes and thought of just allowing semicolons in the URL (I know, I know, not secure) using the snippet below in the same SecurityConfig class.
#Bean
public HttpFirewall allowUrlSemicolonHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlSemicolonHttpFirewall());
}
And voila! The web flow runs on an infinite redirect.
Questions:
Has anyone ever encountered IllegalArgumentException: The session tracking mode [COOKIE] requested for context [/<context-name>] is not supported by that context before? I've searched far and wide and the closest that I could find is this.
Could the reason behind server.servlet.session.tracking-modes=cookie not working be the same as above?
Could the infinite redirect be caused by http.authorizeRequests().anyRequest().permitAll()? I tried using anonymous() but the result was the same.
Is it possible to know which part exactly is causing the infinite redirect?
Please note that allowing semicolons in the URL is working fine and dandy in my localhost, so I have a hunch that what's causing the redirects is SSL-related. In the same way that locally ;jsessionid is not being appended to the URL.
My next step is to try configuring SSL locally in an attempt to replicate the issue. In the meantime, any help would be highly appreciated. My apologies if there's too much information here; I'm willing to repost it as multiple questions if that's necessary.

Spring Security OAuth2 client with authorization_code grant - How to handle token request?

I am building a Spring Boot application using version 2.3.4 with spring-boot-starter-oauth2-client and spring-boot-starter-security dependencies.
I am trying to implement the JIRA Tempo plugin OAuth support.
I have it partially working using the following properties:
spring.security.oauth2.client.registration.tempo.redirect-uri=http://localhost:8080
spring.security.oauth2.client.registration.tempo.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.tempo.client-id=<the-client-id>
spring.security.oauth2.client.registration.tempo.client-secret=<the-client-secret>
spring.security.oauth2.client.registration.tempo.provider=tempo
spring.security.oauth2.client.provider.tempo.authorization-uri=https://mycompany.atlassian.net/plugins/servlet/ac/io.tempo.jira/oauth-authorize/?access_type=tenant_user
spring.security.oauth2.client.provider.tempo.token-uri=https://api.tempo.io/oauth/token/
and this config:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(expressionInterceptUrlRegistry -> expressionInterceptUrlRegistry.anyRequest().authenticated())
.oauth2Login();
}
When I access http://localhost:8080, it redirects to JIRA/Tempo and shows the approval dialog there to grant access to the Tempo data for my application. I can grant access, but after that, it just redirects again to that page instead of showing my own application.
With debugging, I noticed that there is a redirect to http://localhost:8080/?code=.... but Spring Security is not handling that. What else do I need to configure?
I also tried to set some breakpoints in DefaultAuthorizationCodeTokenResponseClient, but they are never hit.
UPDATE:
I changed the redirect-uri to be:
spring.security.oauth2.client.registration.tempo.redirect-uri={baseUrl}/{action}/oauth2/code/{registrationId}
(and I changed the Redirect URIs setting in Tempo to http://localhost:8080/login/oauth2/code/tempo).
This now redirects back to my localhost, but it fails with authorization_request_not_found.
UPDATE 2:
The reason for the authorization_request_not_found seems to be mismatch in HttpSessionOAuth2AuthorizationRequestRepository.removeAuthorizationRequest(HttpServletRequest request) between what is in the authorizationRequests and the stateParameters.
Note how one ends with = and the other with %3D, which makes them not match. It is probably no coincidence that the URL encoding of = is %3D. It is unclear to me if this is something that is a Spring Security problem, or a problem of the Tempo resource server, or still a misconfiguration on my part.
The redirect-uri property should not point to the root of your application, but to the processing filter, where the code after redirect is processed.
Best practice for you would be to leave the redirect-uri alone for the time being. Then it will default to /login/oauth2/code/* and this Url is handled by org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.

401 while trying to access Swagger UI - Springdoc

I was writing spring application.I added swagger into my project but somehow It couldn't run properly.Into my project also has bearer authentication with token. Please give me a hint How I might fix this problem
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.1.44</version>
</dependency>
Actually the problem is in your security setting. All resources/endpoints are protected by default when security is present on class path. What you need to do is to expose all resource that are needed for Swagger UI publicly. To do so you have at least two options. The first is to change or create configuration like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("swagger-ui/**", "swagger-ui**", "/v3/api-docs/**", "/v3/api-docs**");
}
By this you override whole HttpSecurity for mentioned paths means no CORS, CSRF, authorization will be checked.
The other option is to try Havelock. With the library you can expose swagger resource by one annotation.
Disclaimer: I am the founder and tech lead of the project
First of all use the last stable version.
This should help:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.4.4</version>
</dependency>
If the issue still persists, add more relevant information about your code to reproduce it.

Spring Boot 2.0.2 Spring Security how to disable custom Form Login for two endpoints

EDIT:
After several days of trying various Security configuration changes, I punted and put .permitAll() on every endpoint which should authorize/authenticate any request. But even then, although I could freely "browse" any page without authenticating, my device clients were still unable to submit PUT requests to their normal application endpoint.
So now the question is, why can the remote clients successfully submit PUT requests to my app running on the 1.5.4 Spring Boot version but not when "the same app" is running at Spring Boot 2.0.2?
I get a successful "health check" response ("up and running as usual...") when I hit the same "device" endpoint with a GET request from my browser. But the client devices just get ERR_CONNECTION_REFUSED (or similar) when they try to PUT.
/EDIT
This question is related to one I asked about Web Socket migration a couple of days ago, but the web socket part turned out to be a red herring.
The real issue I'm facing is related to Spring Security in SB 2.0.2.
springBootVersion = '2.0.2.RELEASE'
springVersion = '5.0.13.RELEASE'
springSecurityVersion = '5.2.1.RELEASE'
Everything was working the way we needed at SB 1.5.4, but at 2.0.2 I can't seem to restore the necessary behavior. What I need is my custom Form Login applied to all endpoints except /input and /input/auth
This is the only configurer adapter we were using at 1.5.4 (with ACCESS OVERRIDE)
#Configuration
#EnableWebSecurity
//#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#Order(1)// highest priority
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
SimpleAuthenticationManager sam;
#Override
protected void configure(HttpSecurity http) throws Exception {
// false means go to original destination page after login success
boolean alwaysRedirectToSuccessUrl = false;
http.headers().cacheControl().disable();
http.headers().frameOptions().sameOrigin();
http.csrf().ignoringAntMatchers("/input/auth/**");// ignoring WebSocket endpoints (secured by other means)
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.authorizeRequests()
.antMatchers('/widgetInfo/**', '/stats', '/errorCodes').hasAuthority('USER').anyRequest().fullyAuthenticated()
http.formLogin()
.loginPage('/widgetInfo/login')
.loginProcessingUrl("/widgetInfo/fooInfo")
.defaultSuccessUrl("/widgetInfo/fooInfo", alwaysRedirectToSuccessUrl)
.failureUrl("/widgetInfo/login?status=LOGIN_FAILURE").permitAll()
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers('/webjars/**', '/static/**', '/css/**', '/js/**', '/input/**');
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(sam)
}
}
The above configuration works in 2.0.2, except that it is not allowing free access to the /input endpoints. After chasing the red herring for a couple of days, and realizing my misunderstanding, I tried adding another much more lenient configurer adapter as more-or-less described at the bottom of this page
#Configuration
#EnableWebSecurity
#Order(11)// lowest priority
class LenientWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/input/auth/**");// ignoring WebSocket endpoints (secured by other means)
http.authorizeRequests().antMatchers('/input', 'input/auth', '/input/**').permitAll()
}
}
But it's not working, the /input endpoint is not yet freely accessible. What is the issue?
If I swap the #Order, then nothing goes through my custom Form Login.
Answering here just to close loop for future users who might land here.
The problem turned out to be that Spring Boot 1.5.4 would accept "HTTP1.1" requests, but Spring Boot 2.0.2 will not.
Our app sits behind an F5 device that rejects inbound requests if/when the application "healthcheck" requests fail. This is the "healthcheck" request that was working at 1.5.4
GET /myGateway/input HTTP/1.1\r\n
But at 2.0.2, that request was failing. At 2.0.2 the healthcheck request needs to be
GET /myGateway/input \r\n
Therefore, "Spring Security" configuration issues were also a red herring.
EDIT: Apparently, this is/was a known issue with 2.0.x that was fixed in Spring Boot 2.2.x (summer 2019)

Spring Boot CSRF

Tried to implement CSRF protection on the latest Spring Boot.
All the examples on internet are based on user login and authentication, which I do not need.
My site does not have any sections requiring authentication.
I would like
1) Rest requests come from within site. No direct request from outside with wget to be allowed.
2) All pages (routes) must be requested from the index page (/)
Included the security dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
-- Defined users in application.properties (even though, I do not need)
-- App creates _csrf.token .
-- Created class extending WebSecurityConfigurerAdapter with "configure" method overriding.
Tried all suggested filters in "configure". It did not work and finally left it blank.
The problem is that Wget can get api pages directly.
How to prevent it?
I've quickly put together a POC of this configuration:
#Configuration
#EnableWebSecurity
#SpringBootApplication
public class StackoverflowQ40929943Application extends WebSecurityConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(StackoverflowQ40929943Application.class, args);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll();
}
}
The gist of it is Spring Boot + Security will secure all endpoints automatically. Here we explicitly allow requests to all endpoints. But, Spring Boot + Security automatically configures CSRF out of the box which we've left enabled. Thus you get the best of both worlds.
NOTE: You'll probably need to refine this configuration further to meet your needs.
Full Example on GitHub

Resources