com.github.rexsheng.springboot.faster.system.config.SwaggerConfig Maven / Gradle / Ivy
The newest version!
package com.github.rexsheng.springboot.faster.system.config;
import com.fasterxml.jackson.annotation.JsonView;
import com.github.rexsheng.springboot.faster.system.modular.SpringModule;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.tags.Tag;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Configuration
@ConditionalOnClass(OpenAPI.class)
@ConditionalOnProperty(prefix = "app.module",name = "springdoc",havingValue = "true",matchIfMissing = false)
@SpringModule
public class SwaggerConfig {
private final String securityKey="bearer token";
@Bean
public GroupedOpenApi systemApi(ApplicationContext applicationContext) {
return GroupedOpenApi.builder()
.group("system")
.displayName("系统管理")
.pathsToMatch("/sys/**")
// .addOpenApiCustomizer(openApiCustomizer())
.addOpenApiCustomizer(securityOperationCustomizer(applicationContext))
// .addOperationCustomizer(operationCustomizer())
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.displayName("业务接口")
.pathsToExclude("/sys/**")
// .addOpenApiCustomizer(openApiCustomizer())
// .addOperationCustomizer(operationCustomizer())
.build();
}
public OpenApiCustomizer securityOperationCustomizer(ApplicationContext applicationContext){
FilterChainProxy filterChainProxy = (FilterChainProxy)applicationContext.getBean("springSecurityFilterChain", FilterChainProxy.class);
return (openAPI) -> {
openAPI.addTagsItem(new Tag().name("SpringSecurity").description("认证"));
Iterator filterChainIterator = filterChainProxy.getFilterChains().iterator();
while(filterChainIterator.hasNext()) {
SecurityFilterChain filterChain = (SecurityFilterChain)filterChainIterator.next();
Stream filterStream = filterChain.getFilters().stream();
filterStream = filterStream.filter(UsernamePasswordAuthenticationFilter.class::isInstance);
Optional optionalFilter = filterStream.map(UsernamePasswordAuthenticationFilter.class::cast).findAny();
filterStream = filterChain.getFilters().stream();
filterStream = filterStream.filter(DefaultLoginPageGeneratingFilter.class::isInstance);
Optional optionalDefaultLoginPageGeneratingFilter = filterStream.map(DefaultLoginPageGeneratingFilter.class::cast).findAny();
filterStream = filterChain.getFilters().stream();
filterStream = filterStream.filter(LogoutFilter.class::isInstance);
Optional optionalLogoutFilter = filterStream.map(LogoutFilter.class::cast).findAny();
if (optionalFilter.isPresent()) {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = (UsernamePasswordAuthenticationFilter)optionalFilter.get();
Operation operation = new Operation();
Schema> schema = new ObjectSchema();
if(true){
StringSchema stringSchema=new StringSchema();
stringSchema.setDescription("用户名");
stringSchema.setExample("admin");
stringSchema.setNullable(false);
schema.addProperty(usernamePasswordAuthenticationFilter.getUsernameParameter(), stringSchema);
}
if(true){
StringSchema stringSchema=new StringSchema();
stringSchema.setDescription("密码");
stringSchema.setExample("123456");
stringSchema.setNullable(false);
schema.addProperty(usernamePasswordAuthenticationFilter.getPasswordParameter(), stringSchema);
}
if(true){
StringSchema stringSchema=new StringSchema();
stringSchema.setDescription("验证码");
stringSchema.setExample("abcd");
stringSchema.setNullable(false);
schema.addProperty("captcha", stringSchema);
}
String mediaType = "application/x-www-form-urlencoded";
// String mediaType = "application/json";
// if (optionalDefaultLoginPageGeneratingFilter.isPresent()) {
// DefaultLoginPageGeneratingFilter defaultLoginPageGeneratingFilter = (DefaultLoginPageGeneratingFilter)optionalDefaultLoginPageGeneratingFilter.get();
// Field formLoginEnabledField = FieldUtils.getDeclaredField(DefaultLoginPageGeneratingFilter.class, "formLoginEnabled", true);
//
// try {
// boolean formLoginEnabled = (Boolean)formLoginEnabledField.get(defaultLoginPageGeneratingFilter);
// if (formLoginEnabled) {
// mediaType = "application/x-www-form-urlencoded";
// }
// } catch (IllegalAccessException e) {
// e.printStackTrace();
// }
// }
RequestBody requestBody = (new RequestBody()).content((new Content()).addMediaType(mediaType, (new MediaType()).schema(schema)));
operation.requestBody(requestBody);
ApiResponses apiResponses = new ApiResponses();
Map dataProperties=new LinkedHashMap<>();
dataProperties.put("access_token",AnnotationsUtils.resolveSchemaFromType(String.class, openAPI.getComponents(), (JsonView)null,
true,org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().example("jwt token").description("token").build(),
(io.swagger.v3.oas.annotations.media.ArraySchema)null, (ModelConverterContext)null));
dataProperties.put("expires_in",AnnotationsUtils.resolveSchemaFromType(Long.class, openAPI.getComponents(), (JsonView)null,
true,org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().example("3600").description("token过期时间,毫秒").build(),
(io.swagger.v3.oas.annotations.media.ArraySchema)null, (ModelConverterContext)null));
dataProperties.put("refresh_token",AnnotationsUtils.resolveSchemaFromType(String.class, openAPI.getComponents(), (JsonView)null,
true,org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().example("jwt token").description("refresh token").build(),
(io.swagger.v3.oas.annotations.media.ArraySchema)null, (ModelConverterContext)null));
dataProperties.put("refresh_expires_in",AnnotationsUtils.resolveSchemaFromType(Long.class, openAPI.getComponents(), (JsonView)null,
true,org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().example("36000").description("refresh token过期时间,毫秒").build(),
(io.swagger.v3.oas.annotations.media.ArraySchema)null, (ModelConverterContext)null));
dataProperties.put("token_type",AnnotationsUtils.resolveSchemaFromType(String.class, openAPI.getComponents(), (JsonView)null,
true,org.springdoc.core.fn.builders.schema.Builder.schemaBuilder().example("Bearer").description("token类型").build(),
(io.swagger.v3.oas.annotations.media.ArraySchema)null, (ModelConverterContext)null));
Schema dataSchema=new io.swagger.v3.oas.models.media.Schema();
dataSchema.setProperties(dataProperties);
Map responseProperties=new LinkedHashMap<>();
responseProperties.put("code",AnnotationsUtils.resolveSchemaFromType(Integer.class, openAPI.getComponents(), (JsonView)null));
responseProperties.put("data",dataSchema);
responseProperties.put("message",AnnotationsUtils.resolveSchemaFromType(String.class, openAPI.getComponents(), (JsonView)null));
ApiResponse response = (new ApiResponse()).description(HttpStatus.OK.getReasonPhrase()).content((new Content())
.addMediaType("application/json", (new MediaType()).schema(new Schema().properties(responseProperties))));
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), response);
apiResponses.addApiResponse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), (new ApiResponse()).description(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()));
operation.responses(apiResponses);
operation.setSummary("登录");
// operation.addTagsItem("认证");
operation.setTags(Arrays.asList("SpringSecurity"));
try {
Field requestMatcherField = AbstractAuthenticationProcessingFilter.class.getDeclaredField("requiresAuthenticationRequestMatcher");
requestMatcherField.setAccessible(true);
RequestMatcher requestMatcher = (RequestMatcher)requestMatcherField.get(usernamePasswordAuthenticationFilter);
requestMatcherField.setAccessible(false);
List requestMatcherDetails=extract(requestMatcher);
if(requestMatcherDetails!=null && requestMatcherDetails.size()>0){
requestMatcherDetails.stream().collect(Collectors.groupingBy(a->a.getPattern())).forEach((k,v)->{
PathItem pathItem=new PathItem();
for(RequestMatcherDetail requestMatcherDetail:v){
if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.GET)){
pathItem.get(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.POST)){
pathItem.post(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.PUT)){
pathItem.put(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.DELETE)){
pathItem.delete(operation);
}
}
openAPI.getPaths().addPathItem(k, pathItem);
});
}
} catch (IllegalAccessException | ClassCastException | NoSuchFieldException e) {
e.printStackTrace();
}
}
if (optionalLogoutFilter.isPresent()) {
LogoutFilter logoutFilter = (LogoutFilter)optionalLogoutFilter.get();
Operation operation = new Operation();
Schema> schema = new ObjectSchema();
String mediaType = "application/json";
RequestBody requestBody = (new RequestBody()).content((new Content()).addMediaType(mediaType, (new MediaType()).schema(schema)));
operation.requestBody(requestBody);
ApiResponses apiResponses = new ApiResponses();
Map responseProperties=new LinkedHashMap<>();
responseProperties.put("code",AnnotationsUtils.resolveSchemaFromType(Integer.class, openAPI.getComponents(), (JsonView)null));
responseProperties.put("data",schema);
responseProperties.put("message",AnnotationsUtils.resolveSchemaFromType(String.class, openAPI.getComponents(), (JsonView)null));
ApiResponse response = (new ApiResponse()).description(HttpStatus.OK.getReasonPhrase()).content((new Content())
.addMediaType("application/json", (new MediaType()).schema(new Schema().properties(responseProperties))));
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), response);
apiResponses.addApiResponse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), (new ApiResponse()).description(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()));
operation.responses(apiResponses);
operation.setSummary("登出");
operation.setTags(Arrays.asList("SpringSecurity"));
try {
Field requestMatcherField = LogoutFilter.class.getDeclaredField("logoutRequestMatcher");
requestMatcherField.setAccessible(true);
RequestMatcher requestMatcher = (RequestMatcher)requestMatcherField.get(logoutFilter);
requestMatcherField.setAccessible(false);
List requestMatcherDetails=extract(requestMatcher);
if(requestMatcherDetails!=null && requestMatcherDetails.size()>0){
requestMatcherDetails.stream().collect(Collectors.groupingBy(a->a.getPattern())).forEach((k,v)->{
PathItem pathItem=new PathItem();
for(RequestMatcherDetail requestMatcherDetail:v){
if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.GET)){
pathItem.get(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.POST)){
pathItem.post(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.PUT)){
pathItem.put(operation);
}
else if(requestMatcherDetail.getHttpMethod().equals(HttpMethod.DELETE)){
pathItem.delete(operation);
}
}
openAPI.getPaths().addPathItem(k, pathItem);
});
}
} catch (IllegalAccessException | ClassCastException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
};
}
private List extract(RequestMatcher requestMatcher) throws NoSuchFieldException, IllegalAccessException {
if(requestMatcher instanceof OrRequestMatcher orRequestMatcher){
return extractRequestMatcher(orRequestMatcher);
}
if(requestMatcher instanceof AntPathRequestMatcher antPathRequestMatcher){
return Arrays.asList(extractRequestMatcher(antPathRequestMatcher));
}
return null;
}
private List extractRequestMatcher(OrRequestMatcher requestMatcher) throws NoSuchFieldException, IllegalAccessException {
List result=new ArrayList<>();
Field requestMatcherField = OrRequestMatcher.class.getDeclaredField("requestMatchers");
requestMatcherField.setAccessible(true);
List requestMatchers = (List)requestMatcherField.get(requestMatcher);
requestMatcherField.setAccessible(false);
if(requestMatchers!=null && requestMatchers.size()>0){
for (RequestMatcher item : requestMatchers) {
if(item instanceof AntPathRequestMatcher antPathRequestMatcher){
result.add(extractRequestMatcher(antPathRequestMatcher));
}
}
}
return result;
}
private RequestMatcherDetail extractRequestMatcher(AntPathRequestMatcher requestMatcher) throws NoSuchFieldException, IllegalAccessException {
Field httpMethodField = AntPathRequestMatcher.class.getDeclaredField("httpMethod");
httpMethodField.setAccessible(true);
HttpMethod httpMethod = (HttpMethod)httpMethodField.get(requestMatcher);
httpMethodField.setAccessible(false);
RequestMatcherDetail result=null;
if(httpMethod == null || httpMethod.equals(HttpMethod.GET)){
result=new RequestMatcherDetail(requestMatcher.getPattern(),HttpMethod.GET);
}
if(httpMethod == null || httpMethod.equals(HttpMethod.POST)){
result=new RequestMatcherDetail(requestMatcher.getPattern(),HttpMethod.POST);
}
if(httpMethod == null || httpMethod.equals(HttpMethod.PUT)){
result=new RequestMatcherDetail(requestMatcher.getPattern(),HttpMethod.PUT);
}
if(httpMethod == null || httpMethod.equals(HttpMethod.DELETE)){
result=new RequestMatcherDetail(requestMatcher.getPattern(),HttpMethod.DELETE);
}
return result;
}
public static class RequestMatcherDetail{
private String pattern;
private HttpMethod httpMethod;
public RequestMatcherDetail(String pattern, HttpMethod httpMethod) {
this.pattern = pattern;
this.httpMethod = httpMethod;
}
public String getPattern() {
return pattern;
}
public HttpMethod getHttpMethod() {
return httpMethod;
}
}
public OpenApiCustomizer openApiCustomizer(){
return openAPI->openAPI.components(new Components().addSecuritySchemes(securityKey,
new SecurityScheme().type(SecurityScheme.Type.HTTP).description("JWT认证").scheme("bearer").bearerFormat("JWT")));
}
private OperationCustomizer operationCustomizer(){
return (operation, handlerMethod) -> {
operation.addSecurityItem(new SecurityRequirement().addList(securityKey));
return operation;
};
}
@Bean
public OpenAPI springDocOpenAPI() {
return new OpenAPI()
.info(info())
// .addTagsItem(new Tag().name("mytag"))
.externalDocs(externalDocumentation())
.components(new Components().addSecuritySchemes(securityKey,
new SecurityScheme().type(SecurityScheme.Type.HTTP).description("JWT认证").scheme("bearer").bearerFormat("JWT"))
// .addParameters("Authorization2", new Parameter().in("header").schema(new StringSchema()).name("Authorization2"))
// .addHeaders("Authorization",new Header().description("Bearer token").schema(new StringSchema()))
)
.addSecurityItem(new SecurityRequirement().addList(securityKey))
;
}
private License license() {
return new License()
.name("Apache 2.0")
.url("http://springdoc.org");
}
private Info info(){
return new Info()
.title("springboot-faster-API")
.description("后台管理系统")
.version("v1.0.0")
.license(license());
}
private ExternalDocumentation externalDocumentation() {
return new ExternalDocumentation()
.description("GITEE开源")
.url("https://gitee.com/shengxp_760");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy