I'm launching my springboot webapp and navigating to localhost/api/myservice/swagger-ui/index.html, but getting a 401 Unauthorized. I've done all the usual things from now countless answers on SO, but none seem to apply specifically to this pairing of spring and spring doc. One limitation I have is Java 1.8, else I would use newer versions of software. Handcuffs.
Short version: spring-boot: 2.7.18 spring-doc: 1.8.0
pom.xml snippets:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
When I checked, the oldest version of springboot for springdocs was past Java 1.8.
application.yml snippets:
springdoc:
api-docs:
path: /api-docs/**
swagger-ui:
enabled = true
path = /swagger-ui.html
tryItOutEnabled = false
filter = false
syntaxHighlight.activated = true
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
server:
servlet:
context-path: /api/myservice
SwaggerConfig.java snippets:
@Configuration
public class SwaggerConfig {
@Bean
public GroupedOpenApi api() {
return GroupedOpenApi.builder()
.group("my/package/**")
.pathsToMatch("/**")
.packagesToExclude("/error.**")
.build();
}
@Bean
public OpenAPI apiInfo() {
final String securitySchemeName = "bearerAuth";
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
.components(new Components().addSecuritySchemes(
securitySchemeName,
new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.in(SecurityScheme.In.HEADER)
.scheme("bearer")
.bearerFormat("JWT")
))
.info(new Info()
.title(title)
.version(version)
.description("")
)
.servers(Collections.singletonList(
new Server()
.url(contextPath)
.description("Default Server URL")
));
}
}
SpringConfiguration.java snippets:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// I have tried very many things here, but I don't see how it could get much more permissive than this ;)
http
.csrf().disable()
.authorizeRequests().anyRequest().permitAll();
return http
.build();
}
@Bean
public RequestMatcher requestMatcher() {
this.log.debug("Creating request matcher");
List<RequestMatcher> requestMatchers = new ArrayList<>();
requestMatchers.add(new AntPathRequestMatcher("/**"));
return new OrRequestMatcher(requestMatchers);
}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter(
RequestMatcher requestMatcher,
AuthenticationConfiguration authenticationConfiguration)
throws Exception {
CustomAuthenticationFilter result = new CustomAuthenticationFilter(requestMatcher);
result.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
return result;
}
}
When I uncomment the CustomAuthenticationFilter stuff, I get the 401 Unauthorized hitting localhost/api/myservice/swagger-ui/index.html, because that class is being applied, but when I comment it out, I hit the swagger page just fine.
I'm new to this version of SpringBoot and new to Swagger 3 and new to springdocs. I have this working in other microservices that use a FilterRegistrationBean instead of something like CustomAuthenticationFilter, and I'm not sure why the same configurations don't work in this repository. Bonus points if this can be done without disabling csrf.
When I navigate to localhost/api/myservice/swagger-ui/index.html I get a 401 Unauthorized. I expect the Swagger page to load without needing authorization.
When I comment out the CustomAuthenticationFilter bean, I get the expected result. I'm not sure why beyond being certain that the CustomAuthenticationFilter is being used to secure Swagger pages when I don't want it to.
I have also tried
@Bean
public WebSecurityCustomizer webSecurityCustomizer(@Value("${server.servlet.context-path}") String contextPath) {
return web -> web
.ignoring()
.antMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", contextPath + "/swagger-ui/**");
}
which issues printed warnings on the console about using authorizeHttpRequests instead. And I've tried that as well.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
//.authorizeHttpRequests()
.antMatchers(
"/v2/api-docs",
"/v3/api-docs/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/swagger-ui/index.html",
contextPath + "/swagger-ui/index.html",
"/configuration/ui",
"/configuration/security",
"/webjars/**",
"/api/**",
contextPath + "/swagger-ui/**"
).permitAll()
.and().authorizeRequests().antMatchers("/api/**").authenticated();
return http.build();
}
first thing you have to do is to exclude swagger endpoint from your security context (SecurityFilterChain), you already did the configuration in WebSecurityCustomizer , no need to do it again .
what you need is :
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) ->
web.ignoring()
.requestMatchers(
new AntPathRequestMatcher("/user/**"),
new AntPathRequestMatcher("/v3/api-docs/**"),
new AntPathRequestMatcher("/swagger-ui/**"),
new AntPathRequestMatcher("/swagger-ui.html")
)
;
}
application.properties (you can change it to yml) :
springdoc.api-docs.enabled=true
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.swagger-ui.enabled=true
springdoc.swagger-ui.operationsSorter=method
springdoc.swagger-ui.tryItOutEnabled=true
springdoc.swagger-ui.filter=true
springdoc.swagger-ui.tagsSorter=alpha
springdoc.api-docs.path=/v3/api-docs
finaly you can keep your OpenAPI() as it is or :
@OpenAPIDefinition(
info = @Info(
contact = @Contact(
name = ""
),
description = "",
title = "",
version = "",
license = @License(
name = "",
url = ""
),
termsOfService = "Terms of service"
),
servers = {
@Server(
description = "Local ENV",
url = ""
),
@Server(
description = "PROD ENV",
url = ""
),
@Server(
description = "TEST ENV",
url = ""
)
},
security = {
@SecurityRequirement(
name = "bearerAuth"
)
}
)
@SecurityScheme(
name = "bearerAuth",
description = "JWT auth description",
scheme = "bearer",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
in = SecuritySchemeIn.HEADER
)
@Configuration
public class SwaggerConfig {
@Bean
public GroupedOpenApi api() {
return GroupedOpenApi.builder()
.group("us/cargosphere/**")
.pathsToMatch("/**")
.packagesToExclude("/error.**")
.build();
}
}