Swagger 之 Starter 编写

这里并没有使用 starter 的自定加载而是使用 注解进行开关 Swagger 服务,这样方便选择性的打开或者关闭 Swagger 服务

没有过多的解释,请见谅,直接上代码:

码云中有整合的案例: https://gitee.com/chxlay/be-helpful/tree/master/behelpful-common/behelpful-swagger/src/main 

依赖:

<dependencies>
    <!--swagger 依赖-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>your version</version>
    </dependency>
    <!-- swagger Ui页面 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>your version</version>
    </dependency>
    <!--knife4j是为Java MVC框架集成Swagger生成Api文档的增强-->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-micro-spring-boot-starter</artifactId>
        <version>your version</version>
    </dependency>
    <!-- 文档 Ui页面 -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-ui</artifactId>
        <version>your version</version>
    </dependency>
</dependencies>

属性配置:

swagger:
  title: Behelpful Swagger API
  license: Powered By Behelpful
  licenseUrl: https://www.xxx.com
  terms-of-service-url: https://www.xxx.com
 
  # 文档上下文主体描述属性信息
  contact:
    email: xxx@gmail.com
    url: https://www.xxx.com
 
  # Swagger 权限认证配置
  authorization:
    name: Authorization
    auth-regex: ^.*$
    authorization-scope-list:
      - scope: server
        description: server all
    token-url-list:
      # 调用权限认证的模块的接口配置
      - http://behelpful-gateway:9999/admin/login
 
  enabled: true

属性文件配置类:

@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
   /**
    * 是否开启swagger
    */
   private Boolean enabled;
   /**
    * swagger会解析的包路径
    **/
   private String basePackage = "";
   /**
    * swagger会解析的url规则
    **/
   private List<String> basePath = new ArrayList<>();
   /**
    * 在basePath基础上需要排除的url规则
    **/
   private List<String> excludePath = new ArrayList<>();
   /**
    * 标题
    **/
   private String title = "";
   /**
    * 描述
    **/
   private String description = "";
   /**
    * 版本
    **/
   private String version = "";
   /**
    * 许可证
    **/
   private String license = "";
   /**
    * 许可证URL
    **/
   private String licenseUrl = "";
   /**
    * 服务条款URL
    **/
   private String termsOfServiceUrl = "";
   /**
    * host信息
    **/
   private String host = "";
   /**
    * 联系人信息
    */
   private Contact contact = new Contact();
   /**
    * 全局统一鉴权配置
    **/
   private Authorization authorization = new Authorization();
 
   @Data
   @NoArgsConstructor
   public static class Contact {
      /**
       * 联系人
       **/
      private String name = "";
      /**
       * 联系人url
       **/
      private String url = "";
      /**
       * 联系人email
       **/
      private String email = "";
   }
 
   @Data
   @NoArgsConstructor
   public static class Authorization {
      /**
       * 鉴权策略ID,需要和SecurityReferences ID保持一致
       */
      private String name = "Authorization";
      /**
       * 需要开启鉴权URL的正则
       */
      private String authRegex = "^.*$";
      /**
       * 鉴权作用域列表
       */
      private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
 
      private List<String> tokenUrlList = new ArrayList<>();
   }
 
   @Data
   @NoArgsConstructor
   public static class AuthorizationScope {
      /**
       * 作用域名称
       */
      private String scope = "";
      /**
       * 作用域描述
       */
      private String description = "";
   }
 
}

配置类:

/**官方文档3.0版本@see {https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md}
 * swagger配置 禁用方法1:使用注解@Profile({"dev","test"})
 * 表示在开发或测试环境开启,而在生产关闭。(推荐使用) 禁用方法2:使用注解@ConditionalOnProperty(name = "swagger.enable",
 * havingValue = "true") 然后在测试配置或者开发配置中添加swagger.enable=true即可开启,生产环境不填则默认关闭Swagger.
 */
@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration {

   /**
    * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
    */
   private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");

   private static final String BASE_PATH = "/**";

   @Autowired
   private SwaggerProperties swaggerProperties;
  

   @Bean
   public Docket api(SwaggerProperties swaggerProperties) {
      // base-path处理
      if (swaggerProperties.getBasePath().isEmpty()) {
         swaggerProperties.getBasePath().add(BASE_PATH);
      }
      List<Predicate<String>> basePath = new ArrayList();
      swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));

      // exclude-path处理
      if (swaggerProperties.getExcludePath().isEmpty()) {
         swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
      }
      List<Predicate<String>> excludePath = new ArrayList<>();
      swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));

      // 版本请求头处理
      ParameterBuilder versionPar = new ParameterBuilder();
      List<Parameter> pars = new ArrayList<>();
      versionPar.name("VERSION").description("灰度路由版本信息").modelRef(new ModelRef("string")).parameterType("header")
            .required(false).build();
      pars.add(versionPar.build());

      // noinspection Guava
      return new Docket(DocumentationType.SWAGGER_2)
            .host(swaggerProperties.getHost())
            // 文档上下文主体描述信息
            .apiInfo(this.apiInfo(swaggerProperties)).globalOperationParameters(pars)
            // 获取Docket 中的选择器,返回ApiSelectorBuilder

            .select()
            // Api 的规则,断言
            .apis(
                  // 设定扫描那个包(及子包)中的注解
                  RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())
            )
            // 类或者方法上含有此直接的不进行文档生成
            .apis(
                  // 添加一个断言,Predicates.not()取反的意思,
                  Predicates.not(
                        // 当方法或者类上含有此注解时返回 true 表示隐藏 接口,不生成Api文档及测试,不用自己写,可使用Swagger 注解自带的 hidden=true
                        RequestHandlerSelectors.withClassAnnotation(HideApi.class)
                  )
            )
            // 路径返回约束
            .paths(
                  Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath))
            )
            // 使用正则表达式匹配,约束生成Api文档的路径地址,.代表任意字符串,*代表 0~n个任意字符
            .paths(PathSelectors.regex("/xxx/.*"))
            .build()
            // 权限认证配置
            .securitySchemes(Collections.singletonList(this.securitySchema()))
            .securityContexts(Collections.singletonList(this.securityContext())).pathMapping("/");
   }

/**
     * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
     *
     * @return
     */
    private List<SecurityContext> securityContext() {
        SecurityContext securityContext = new SecurityContext(
                this.defaultAuth(),
                PathSelectors.regex(swaggerProperties.getAuthorization().getAuthRegex())
        );
        return ICollUtil.asList(securityContext);
    }

    /**
     * 默认的全局鉴权策略
     *
     * @return
     */
    private List<SecurityReference> defaultAuth() {
        List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))
                );

        AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];

        authorizationScopes = authorizationScopeList.toArray(authorizationScopes);

        SecurityReference securityReference = new SecurityReference(swaggerProperties.getAuthorization().getName(), authorizationScopes);

        return ICollUtil.asList(securityReference);
    }

    /**
     * 权限认证
     *
     * @return
     */
    private List<OAuth> securitySchema() {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));

        List<GrantType> grantTypes = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getTokenUrlList()
                // 权限认证调用服务的 Api,如:http://xxx/xxx/login
                .forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
        OAuth oAuth = new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
        return ICollUtil.asList(oAuth);
    }


    /**
     * 另一种权鉴认证
     *
     * @return
     */
    private List<ApiKey> securitySchema() {
        // ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        ApiKey apiKey = new ApiKey("Authorization-Token", "Authorization-Token", "header");
        return ICollUtil.asList(apiKey);
    }

    /**
     * 文档上下文主体描述属性信息对象构建
     *
     * @param swaggerProperties
     * @return
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .contact(
                        new Contact(
                                swaggerProperties.getContact().getName(),
                                swaggerProperties.getContact().getUrl(),
                                swaggerProperties.getContact().getEmail()
                        )
                )
                .version(swaggerProperties.getVersion())
                .build();
    }
}

    /**
     * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
     *
     * @return
     */
    private List<SecurityContext> securityContext() {
        SecurityContext securityContext = new SecurityContext(
                this.defaultAuth(),
                PathSelectors.regex(swaggerProperties.getAuthorization().getAuthRegex())
        );
        return ICollUtil.asList(securityContext);
    }

    /**
     * 默认的全局鉴权策略
     *
     * @return
     */
    private List<SecurityReference> defaultAuth() {
        List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))
                );

        AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];

        authorizationScopes = authorizationScopeList.toArray(authorizationScopes);

        SecurityReference securityReference = new SecurityReference(
                swaggerProperties.getAuthorization().getName(),
                authorizationScopes
        );

        return ICollUtil.asList(securityReference);
    }

    /**
     * 权限认证
     *
     * @return
     */
    private List<OAuth> securitySchema() {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));

        List<GrantType> grantTypes = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getTokenUrlList()
                // 权限认证调用服务的 Api,如:http://xxx/xxx/login
                .forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
        OAuth oAuth = new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
        return ICollUtil.asList(oAuth);
    }


    /**
     * 另一种权鉴认证
     *
     * @return
     */
    private List<ApiKey> securitySchema() {
        // ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        ApiKey apiKey = new ApiKey("Authorization-Token", "Authorization-Token", "header");
        return ICollUtil.asList(apiKey);
    }

    /**
     * 文档上下文主体描述属性信息对象构建
     *
     * @param swaggerProperties
     * @return
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .contact(
                        new Contact(
                                swaggerProperties.getContact().getName(),
                                swaggerProperties.getContact().getUrl(),
                                swaggerProperties.getContact().getEmail()
                        )
                )
                .version(swaggerProperties.getVersion())
                .build();
    }
}

 如:项目中实际使用的案例:

@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
@EnableConfigurationProperties(value = SwaggerProperties.class)
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerConfig {
    /**
     * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
     */
    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");

    private static final String BASE_PATH = "/**";

    @Autowired
    private SwaggerProperties swaggerProperties;

    @Bean
    public Docket docket() {
        // base-path处理
        if (swaggerProperties.getBasePath().isEmpty()) {
            swaggerProperties.getBasePath().add(BASE_PATH);
        }
        List<com.google.common.base.Predicate<String>> basePath = new ArrayList();
        swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));

        // exclude-path处理
        if (swaggerProperties.getExcludePath().isEmpty()) {
            swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
        }
        List<Predicate<String>> excludePath = new ArrayList<>();
        swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));

        // 版本请求头处理
        ParameterBuilder versionPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<>();
        versionPar
                .name("VERSION")
                .description("灰度路由版本信息")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(versionPar.build());

        // noinspection Guava
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName(swaggerProperties.getGroupName())
                .host(swaggerProperties.getHost())
                // 是否开启文档生成
                .enable(swaggerProperties.getEnabled())
                .apiInfo(this.apiInfo(swaggerProperties))
                .globalOperationParameters(pars)

                .select()
                // Api 的规则,断言
                .apis(
                        // 设定扫描那个包(及子包)中的注解
                        RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())
                )
                // 路径返回约束
                .paths(
                        Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath))
                )
                .build()
                // 权限认证
                .securitySchemes(this.securitySchema())
                .securityContexts(this.securityContext())
                .pathMapping("/");

        log.info("My Swagger | 配置完成");

        return docket;
    }

    /**
     * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
     *
     * @return
     */
    private List<SecurityContext> securityContext() {
        SecurityContext securityContext = new SecurityContext(
                this.defaultAuth(),
                PathSelectors.regex(swaggerProperties.getAuthorization().getAuthRegex())
        );
        return ICollUtil.asList(securityContext);
    }

    /**
     * 默认的全局鉴权策略
     *
     * @return
     */
    private List<SecurityReference> defaultAuth() {
        List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription()))
                );

        AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];

        authorizationScopes = authorizationScopeList.toArray(authorizationScopes);

        SecurityReference securityReference = new SecurityReference(
                swaggerProperties.getAuthorization().getName(),
                authorizationScopes
        );

        return ICollUtil.asList(securityReference);
    }

    /**
     * 权限认证
     *
     * @return
     */
   /* private List<OAuth> securitySchema() {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getAuthorizationScopeList()
                .forEach(authorizationScope -> authorizationScopeList.add(
                        new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));

        List<GrantType> grantTypes = new ArrayList<>();
        swaggerProperties.getAuthorization()
                .getTokenUrlList()
                // 权限认证调用服务的 Api,如:http://xxx/xxx/login
                .forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
        OAuth oAuth = new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
        return ICollUtil.asList(oAuth);
    }*/


    /**
     * 另一种权鉴认证
     *
     * @return
     */
    private List<ApiKey> securitySchema() {
        // ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        ApiKey apiKey = new ApiKey("Authorization-Token", "Authorization-Token", "header");
        return ICollUtil.asList(apiKey);
    }

    /**
     * 文档上下文主体描述属性信息对象构建
     *
     * @param swaggerProperties
     * @return
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense())
                .licenseUrl(swaggerProperties.getLicenseUrl())
                .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                .contact(
                        new Contact(
                                swaggerProperties.getContact().getName(),
                                swaggerProperties.getContact().getUrl(),
                                swaggerProperties.getContact().getEmail()
                        )
                )
                .version(swaggerProperties.getVersion())
                .build();
    }
}

 

自定义注解,不推荐使用,推荐使用 Swagger 内置注解处理 hidden=true

@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface HideApi {
 
}

Swagger 启动控制开关注解:

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({ SwaggerAutoConfiguration.class })
public @interface EnableMySwagger2 {
 
}

 

posted @ 2021-08-21 23:00  Vermeer  阅读(170)  评论(0)    收藏  举报