swagger是一套基於RESTful 風格的 Web 服務。Swagger能成爲最受歡迎的REST APIs文檔生成工具之一,有如下幾個緣由:html
在初次使用swagger時確實體會到了其方便快捷的對文檔處理特有的優點,雖然有點對其頁面展現方式有點無力吐槽,可是對於後端開發來講這無疑比那些手動去寫接口文檔所帶來的便捷是無與倫比的。前端
在整合spingboot時幾乎沒有任何問題,方便簡潔幾句註解就能夠解決restful風格的接口文檔處理問題。java
不得不提在此以前,我也在項目中本身寫過一個基於註釋去解析的API接口文檔,無疑那是簡陋的。起初那只是爲jfinal寫的一個接口文檔,固然前端頁面很漂亮用的是react去寫的,源代碼解析也是本身去着手編寫的。可是當接觸到springboot以後,接觸到restful風格接口以後,確實捉襟見肘了,如今也懶得去改那些接口解析讓它去支持這些風格,由於那樣將會付出很多時間,因而選擇了swagger。react
好了很少說,直接上手說說哪些在使用swagger時遇到的問題。spring
在整合springcloud問題時,因爲多項目時怎麼去統一swagger文檔確實是問題所在,當初也是翻遍了好多網上大神的經驗以後得出的一個方案。此處直接上代碼,只須要一段小小的配置便可實現。後端
@Component @Primary public class DocumentationConfig implements SwaggerResourcesProvider { @Override public List<SwaggerResource> get() { List resources = new ArrayList<>(); resources.add(swaggerResource("WEB_SERVICE 服務", "/v2/api-docs", "2.0")); resources.add(swaggerResource("USER_SERVICE 服務", "/api/base/v2/api-docs", "2.0")); resources.add(swaggerResource("PRODUCT_SERVICE 服務", "/api/pc/v2/api-docs", "2.0")); return resources; } private SwaggerResource swaggerResource(String name, String location, String version) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion(version); return swaggerResource; } }
接下來最重要的環節,如何解決jwt受權時問題,網上給出的解決方案中,突出的一個是在統一的方法上去添加請求頭 header。但這無疑是很麻煩的一件事,不可能由於涉及到受權問題,每一個方法調用時都要去添加token吧。api
通過google又結合了大神給出的新資訊終於找到了另外一個方案,特此貼上代碼。springboot
代碼摘錄:https://cloud.tencent.com/developer/article/1335430restful
@EnableSwagger2 @Configuration @RefreshScope public class Swagger2 { //是否開啓swagger,正式環境通常是須要關閉的,可根據springboot的多環境配置進行設置 @Value(value = "${swagger.enabled}") Boolean swaggerEnabled; @Value(value = "${swagger.version}") String version; @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) // 是否開啓 .enable(swaggerEnabled).select() // 掃描的路徑包 .apis(RequestHandlerSelectors.basePackage("com.jeff.regan.userservice.controller")) // 指定路徑處理PathSelectors.any()表明全部的路徑 .paths(PathSelectors.any()).build() .pathMapping("/") .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } private List<ApiKey> securitySchemes() { List<ApiKey> apiKeyList= new ArrayList(); apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); return apiKeyList; } private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts=new ArrayList<>(); securityContexts.add( SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex("^(?!auth).*$")) .build()); return securityContexts; } List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> securityReferences=new ArrayList<>(); securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); return securityReferences; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("用戶管理 USER-SERVICE API接口文檔示例") .version(version) .description("用戶管理接口服務") .build(); } }
在服務與服務之間調用時,遇到token受權 header失效處理。添加fegin攔截配置。多線程
@Component public class FeignHeaderInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); template.header(HttpHeaders.AUTHORIZATION, request.getHeader(HttpHeaders.AUTHORIZATION)); } }
fegin 配置
/** * 用戶管理 * @author zhangby * @date 2019-03-29 16:53 */ @FeignClient(value = "user-service",fallback = UserServiceHystrix.class,configuration = FeignHeaderInterceptor.class) public interface UserServiceClient { /** * 獲取用戶 * @param id * @return */ @GetMapping("/user/{id}") ResultPoJo getUser(@PathVariable("id") Long id); }
當多線程異步調用的狀況下,fegin攔截器經過request裏獲取 token會拋異常。緣由request對象已失效,以下是借鑑了一些他人解決的方案。
添加攔截器
/** * jwt攔截器 */ @Component public class JwtInterceptor implements HandlerInterceptor { private final Logger _logger = LoggerFactory.getLogger(this.getClass()); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String authorization = request.getHeader(HttpHeaders.AUTHORIZATION); if (handler == null) { _logger.error("沒有權限"); PrintWriter writer = null; response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=utf-8"); try { writer = response.getWriter(); writer.print(ResultPoJo.create("401").msg("沒有訪問權限").toJson()); } catch (IOException e) { } finally { if (writer != null) { writer.close(); } } return false; } //設置全局變量,在feign調用異步請求時,添加token認證 -> FeignHeaderInterceptor System.setProperty(HttpHeaders.AUTHORIZATION, authorization); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //攔截器結束後,刪除token System.clearProperty(HttpHeaders.AUTHORIZATION); } }
feign攔截器更改
/** * feign 請求統一處理token攔截器問題 * * @author zhangby * @date 2019-04-01 16:26 */ @Component public class FeignHeaderInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { template.header(HttpHeaders.AUTHORIZATION, System.getProperty(HttpHeaders.AUTHORIZATION)); } }