All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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