View Javadoc
1   package cn.home1.oss.lib.swagger.starter;
2   
3   import static cn.home1.oss.lib.swagger.SwaggerUtils.apiInfo;
4   import static com.google.common.base.Predicates.not;
5   import static com.google.common.base.Predicates.or;
6   import static com.google.common.collect.Lists.newArrayList;
7   import static com.google.common.collect.Sets.newLinkedHashSet;
8   import static java.util.stream.Collectors.toList;
9   import static org.apache.commons.lang3.StringUtils.isNotBlank;
10  import static springfox.documentation.builders.PathSelectors.ant;
11  import static springfox.documentation.builders.PathSelectors.regex;
12  
13  import com.google.common.base.Predicate;
14  
15  import cn.home1.oss.boot.autoconfigure.AppUtils;
16  import cn.home1.oss.boot.autoconfigure.ConditionalOnNotEnvProduction;
17  
18  import com.fasterxml.classmate.ResolvedType;
19  
20  import lombok.extern.slf4j.Slf4j;
21  
22  import org.springframework.beans.factory.annotation.Autowired;
23  import org.springframework.beans.factory.annotation.Qualifier;
24  import org.springframework.boot.autoconfigure.AutoConfigureAfter;
25  import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
26  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
29  import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
30  import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
31  import org.springframework.boot.autoconfigure.security.SecurityProperties;
32  import org.springframework.boot.autoconfigure.web.ServerProperties;
33  import org.springframework.context.annotation.Bean;
34  import org.springframework.context.annotation.ComponentScan;
35  import org.springframework.context.annotation.Configuration;
36  import org.springframework.context.annotation.Import;
37  import org.springframework.core.annotation.Order;
38  import org.springframework.core.env.Environment;
39  import org.springframework.security.config.annotation.ObjectPostProcessor;
40  import org.springframework.security.config.annotation.web.builders.WebSecurity;
41  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
42  import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
43  import org.springframework.security.web.util.matcher.OrRequestMatcher;
44  import org.springframework.security.web.util.matcher.RequestMatcher;
45  
46  import springfox.documentation.RequestHandler;
47  import springfox.documentation.builders.RequestHandlerSelectors;
48  import springfox.documentation.spi.DocumentationType;
49  import springfox.documentation.spring.web.SpringfoxWebMvcConfiguration;
50  import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
51  import springfox.documentation.spring.web.plugins.Docket;
52  import springfox.documentation.swagger.configuration.SwaggerCommonConfiguration;
53  import springfox.documentation.swagger2.configuration.Swagger2JacksonModule;
54  
55  import java.util.List;
56  import java.util.Optional;
57  import java.util.Set;
58  
59  /**
60   * Created by Jin Yuliang on 16/8/18.
61   */
62  @AutoConfigureAfter(SecurityAutoConfiguration.class)
63  @ConditionalOnNotEnvProduction
64  @ConditionalOnWebApplication
65  @Configuration
66  @Import({ //
67    SpringfoxWebMvcConfiguration.class, //
68    SwaggerCommonConfiguration.class, //
69    ManagementConfiguration.class, //
70    NoManagementConfiguration.class})
71  @ComponentScan(basePackages = { //
72    "springfox.documentation.swagger2.readers.parameter", //
73    "springfox.documentation.swagger2.web", //
74    "springfox.documentation.swagger2.mappers"})
75  @Slf4j
76  public class Swagger2DocumentationAutoConfiguration {
77  
78    public static final String DOCKET_APPLICATION = "docketApplication";
79  
80    @Autowired(required = false)
81    private ServerProperties serverProperties;
82  
83    @Qualifier(NoManagementConfiguration.MANAGEMENT_PATHS)
84    @Autowired
85    public Predicate<String> managementPaths;
86  
87    @Bean
88    public JacksonModuleRegistrar swagger2Module() {
89      return new Swagger2JacksonModule();
90    }
91  
92    @ConditionalOnMissingBean(name = DOCKET_APPLICATION)
93    @Bean(name = DOCKET_APPLICATION)
94    public Docket docketApplication() {
95      final Docket docket = new Docket(DocumentationType.SWAGGER_2) //
96        .apiInfo(apiInfo("application", "application's endpoints")) //
97        .groupName("api") //
98        .select() //
99        .apis(or(this.applicationAips(), this.springApis())) //
100       .paths(not(this.managementPaths)) //
101       .build();
102     final Optional<ResolvedType> modelResolvedError = ManagementConfiguration.modelResolvedError();
103     return modelResolvedError.isPresent() ? docket.additionalModels(modelResolvedError.get()) : docket;
104   }
105 
106   private Predicate<String> errorPath() {
107     return ant(this.serverProperties.getError().getPath());
108   }
109 
110   private Predicate<String> anyPath() {
111     return regex("/.*");
112   }
113 
114   private Predicate<RequestHandler> applicationAips() {
115     final Predicate<RequestHandler> ossLibApi = RequestHandlerSelectors.basePackage("cn.home1"); // TODO 从此类包取前两段
116     final String applicationPackage = AppUtils.appBasePackage("");
117     return isNotBlank(applicationPackage) ? //
118       or(RequestHandlerSelectors.basePackage(applicationPackage), ossLibApi) : //
119       RequestHandlerSelectors.any();
120   }
121 
122   private Predicate<RequestHandler> springApis() {
123     return RequestHandlerSelectors.basePackage("org.springframework");
124   }
125 
126   @Configuration
127   @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
128   @ConditionalOnBean(ObjectPostProcessor.class)
129   @ConditionalOnProperty(prefix = "security.basic", name = "enabled", matchIfMissing = true)
130   static class SwaggerSecurityConfiguration {
131 
132     @Bean
133     public WebSecurityConfigurerAdapter swaggerSecurityConfigurer() {
134       return new Swagger2DocumentationAutoConfiguration.SwaggerSecurityConfiguration.SwaggerSecurityConfigurer();
135     }
136 
137     @Order(SecurityProperties.BASIC_AUTH_ORDER - 11)
138     private static class SwaggerSecurityConfigurer extends WebSecurityConfigurerAdapter {
139 
140       @Autowired
141       private Environment environment;
142 
143       @Override
144       public void init(final WebSecurity web) throws Exception {
145         final RequestMatcher requestMatcher = swaggerRequestMatcher();
146         if (requestMatcher != null) {
147           web.ignoring().requestMatchers(requestMatcher);
148         }
149         //super.init(web); // don't call super here or there will be a duplicate AnyRequestMatcher.
150       }
151 
152       //@Override
153       //public void configure(final HttpSecurity http) throws Exception {
154       //  final RequestMatcher requestMatcher = swaggerRequestMatcher();
155       //  if (requestMatcher != null) {
156       //    http.requestMatcher(requestMatcher).authorizeRequests().anyRequest().permitAll();
157       //  }
158       //}
159 
160       private RequestMatcher swaggerRequestMatcher() {
161         final Set<String> paths = newLinkedHashSet();
162         // default is: /v2/api-docs
163         paths.add("/webjars/**");
164         paths.add(this.environment.getProperty("springfox.documentation.swagger.v1.path", "/v1/**"));
165         paths.add(this.environment.getProperty("springfox.documentation.swagger.v2.path", "/v2/**"));
166         paths.addAll(newArrayList("/swagger-ui.html", "/swagger-resources/**"));
167 
168         final RequestMatcher requestMatcher;
169         if (!paths.isEmpty()) {
170           final List<RequestMatcher> matchers = paths.stream() //
171             .map(pattern -> new AntPathRequestMatcher(pattern, null)) //
172             .collect(toList());
173           requestMatcher = new OrRequestMatcher(matchers);
174         } else {
175           requestMatcher = null;
176         }
177         return requestMatcher;
178       }
179     }
180   }
181 }