Skip to content

Commit 38f70b1

Browse files
author
bnasslahsen
committed
Make Spring Security login-endpoint automatically visible in SwaggerUI. Fixes #827
1 parent 98bfe31 commit 38f70b1

File tree

13 files changed

+635
-1
lines changed

13 files changed

+635
-1
lines changed

pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
<webjars-locator-core.version>0.45</webjars-locator-core.version>
7575
<gmavenplus-plugin.version>1.8.1</gmavenplus-plugin.version>
7676
<jaxb-impl.version>2.1</jaxb-impl.version>
77+
<javax.jws-api.version>1.1</javax.jws-api.version>
78+
<jjwt.version>0.9.1</jjwt.version>
7779
</properties>
7880

7981
<dependencyManagement>
@@ -132,6 +134,18 @@
132134
<artifactId>jaxb-impl</artifactId>
133135
<version>${jaxb-impl.version}</version>
134136
</dependency>
137+
<dependency>
138+
<groupId>javax.jws</groupId>
139+
<artifactId>javax.jws-api</artifactId>
140+
<version>${javax.jws-api.version}</version>
141+
<scope>test</scope>
142+
</dependency>
143+
<dependency>
144+
<groupId>io.jsonwebtoken</groupId>
145+
<artifactId>jjwt</artifactId>
146+
<version>${jjwt.version}</version>
147+
<scope>test</scope>
148+
</dependency>
135149
</dependencies>
136150
</dependencyManagement>
137151
<dependencies>

springdoc-openapi-common/src/main/java/org/springdoc/core/Constants.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ public final class Constants {
8080
*/
8181
public static final String SPRINGDOC_SCHEMA_RESOLVE_PROPERTIES = "springdoc.api-docs.resolve-schema-properties";
8282

83+
/**
84+
* The constant SPRINGDOC_SHOW_LOGIN_ENDPOINT.
85+
*/
86+
public static final String SPRINGDOC_SHOW_LOGIN_ENDPOINT = "springdoc.show-login-endpoint";
87+
8388
/**
8489
* The constant SPRINGDOC_CACHE_DISABLED.
8590
*/

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfigProperties.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public class SpringDocConfigProperties {
125125
*/
126126
private boolean useFqn;
127127

128+
/**
129+
* The Show login endpoint.
130+
*/
131+
private boolean showLoginEndpoint;
132+
128133
/**
129134
* Is use fqn boolean.
130135
*
@@ -215,6 +220,24 @@ public void setPathsToExclude(List<String> pathsToExclude) {
215220
this.pathsToExclude = pathsToExclude;
216221
}
217222

223+
/**
224+
* Is show login endpoint boolean.
225+
*
226+
* @return the boolean
227+
*/
228+
public boolean isShowLoginEndpoint() {
229+
return showLoginEndpoint;
230+
}
231+
232+
/**
233+
* Sets show login endpoint.
234+
*
235+
* @param showLoginEndpoint the show login endpoint
236+
*/
237+
public void setShowLoginEndpoint(boolean showLoginEndpoint) {
238+
this.showLoginEndpoint = showLoginEndpoint;
239+
}
240+
218241
/**
219242
* Gets packages to scan.
220243
*
@@ -663,6 +686,7 @@ public void setEnabled(boolean enabled) {
663686
}
664687
}
665688

689+
666690
/**
667691
* The type Cache.
668692
* @author bnasslahsen

springdoc-openapi-security/pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
35
<modelVersion>4.0.0</modelVersion>
46
<parent>
57
<artifactId>springdoc-openapi</artifactId>
@@ -42,6 +44,16 @@
4244
<artifactId>jaxb-impl</artifactId>
4345
<scope>test</scope>
4446
</dependency>
47+
<dependency>
48+
<groupId>javax.jws</groupId>
49+
<artifactId>javax.jws-api</artifactId>
50+
<scope>test</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>io.jsonwebtoken</groupId>
54+
<artifactId>jjwt</artifactId>
55+
<scope>test</scope>
56+
</dependency>
4557
</dependencies>
4658

4759
</project>

springdoc-openapi-security/src/main/java/org/springdoc/security/SpringDocSecurityConfiguration.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,36 @@
2020

2121
package org.springdoc.security;
2222

23+
import java.util.Optional;
24+
25+
import io.swagger.v3.oas.models.Operation;
26+
import io.swagger.v3.oas.models.PathItem;
27+
import io.swagger.v3.oas.models.media.Content;
28+
import io.swagger.v3.oas.models.media.MediaType;
29+
import io.swagger.v3.oas.models.media.ObjectSchema;
30+
import io.swagger.v3.oas.models.media.Schema;
31+
import io.swagger.v3.oas.models.media.StringSchema;
32+
import io.swagger.v3.oas.models.parameters.RequestBody;
33+
import io.swagger.v3.oas.models.responses.ApiResponse;
34+
import io.swagger.v3.oas.models.responses.ApiResponses;
35+
import org.springdoc.core.customizers.OpenApiCustomiser;
36+
2337
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2438
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2539
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2640
import org.springframework.context.annotation.Bean;
2741
import org.springframework.context.annotation.Configuration;
42+
import org.springframework.context.annotation.Lazy;
43+
import org.springframework.http.HttpStatus;
2844
import org.springframework.security.core.Authentication;
2945
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3046
import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping;
47+
import org.springframework.security.web.FilterChainProxy;
48+
import org.springframework.security.web.SecurityFilterChain;
49+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
3150

3251
import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
52+
import static org.springdoc.core.Constants.SPRINGDOC_SHOW_LOGIN_ENDPOINT;
3353
import static org.springdoc.core.SpringDocUtils.getConfig;
3454

3555
/**
@@ -65,4 +85,35 @@ SpringSecurityOAuth2Provider springSecurityOAuth2Provider(FrameworkEndpointHandl
6585
return new SpringSecurityOAuth2Provider(oauth2EndpointHandlerMapping);
6686
}
6787
}
88+
89+
@Bean
90+
@ConditionalOnProperty(SPRINGDOC_SHOW_LOGIN_ENDPOINT)
91+
@Lazy(false)
92+
OpenApiCustomiser springSecurityLoginEndpointCustomiser(FilterChainProxy filterChainProxy) {
93+
return openAPI -> {
94+
for (SecurityFilterChain filterChain : filterChainProxy.getFilterChains()) {
95+
Optional<UsernamePasswordAuthenticationFilter> optionalFilter =
96+
filterChain.getFilters().stream()
97+
.filter(filterVar -> filterVar instanceof UsernamePasswordAuthenticationFilter)
98+
.map(filter -> (UsernamePasswordAuthenticationFilter) filter)
99+
.findAny();
100+
if (optionalFilter.isPresent()) {
101+
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = optionalFilter.get();
102+
Operation operation = new Operation();
103+
Schema<?> schema = new ObjectSchema()
104+
.addProperties(usernamePasswordAuthenticationFilter.getUsernameParameter(), new StringSchema())
105+
.addProperties(usernamePasswordAuthenticationFilter.getPasswordParameter(), new StringSchema());
106+
RequestBody requestBody = new RequestBody().content(new Content().addMediaType("loginRequestBody", new MediaType().schema(schema)));
107+
operation.requestBody(requestBody);
108+
ApiResponses apiResponses = new ApiResponses();
109+
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), new ApiResponse().description(HttpStatus.OK.getReasonPhrase()));
110+
apiResponses.addApiResponse(String.valueOf(HttpStatus.FORBIDDEN.value()), new ApiResponse().description(HttpStatus.FORBIDDEN.getReasonPhrase()));
111+
operation.responses(apiResponses);
112+
operation.addTagsItem("login-endpoint");
113+
PathItem pathItem = new PathItem().post(operation);
114+
openAPI.getPaths().addPathItem("/login", pathItem);
115+
}
116+
}
117+
};
118+
}
68119
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app6;
20+
21+
import test.org.springdoc.api.AbstractSpringDocTest;
22+
import test.org.springdoc.api.app6.security.MyUserDetailsService;
23+
24+
import org.springframework.boot.autoconfigure.SpringBootApplication;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.test.context.TestPropertySource;
27+
28+
@TestPropertySource(properties = "springdoc.show-login-endpoint=true")
29+
public class SpringDocApp6Test extends AbstractSpringDocTest {
30+
31+
@SpringBootApplication(scanBasePackages = { "test.org.springdoc.api.configuration,test.org.springdoc.api.app6" })
32+
static class SpringDocTestApp {
33+
@Bean
34+
MyUserDetailsService userDetailsService() {
35+
return new MyUserDetailsService();
36+
}
37+
38+
}
39+
40+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package test.org.springdoc.api.app6;
2+
3+
import io.swagger.v3.oas.models.Components;
4+
import io.swagger.v3.oas.models.OpenAPI;
5+
import io.swagger.v3.oas.models.info.Info;
6+
import io.swagger.v3.oas.models.security.SecurityRequirement;
7+
import io.swagger.v3.oas.models.security.SecurityScheme;
8+
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
12+
13+
@Configuration
14+
public class SpringDocConfig {
15+
16+
17+
@Bean
18+
public OpenAPI myOpenAPI() {
19+
final String securitySchemeName = "bearerAuth";
20+
return new OpenAPI().info(new Info().title("My MWE API")
21+
.description("This document specifies the API")
22+
.version("v23"))
23+
.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
24+
.components(new Components().addSecuritySchemes(securitySchemeName,
25+
new SecurityScheme()
26+
.type(SecurityScheme.Type.HTTP)
27+
.scheme("bearer")
28+
.bearerFormat("JWT")));
29+
}
30+
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package test.org.springdoc.api.app6.controllers;
2+
3+
import java.util.List;
4+
5+
import io.swagger.v3.oas.annotations.Operation;
6+
import io.swagger.v3.oas.annotations.Parameter;
7+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.RequestParam;
13+
import org.springframework.web.bind.annotation.RestController;
14+
15+
16+
@RestController
17+
@RequestMapping("/fax")
18+
@Tag(name = "Fax stuff", description = "For managing fax machines.")
19+
public class MyController {
20+
21+
@Operation(summary = "Get information about currently existing fax machines")
22+
@ApiResponse(responseCode = "200", description = "list of existing fax machines")
23+
@GetMapping("list")
24+
public List<String> getFaxList(@RequestParam(name = "vendorName", required = false)
25+
@Parameter(description = "vendor name to restrict the list") String vendorFilter) {
26+
27+
return null;
28+
}
29+
30+
}

0 commit comments

Comments
 (0)