2019独角兽企业重金招聘Python工程师标准>>>
关于 swagger
swagger是一套基于RESTful 风格的 Web 服务。Swagger能成为最受欢迎的REST APIs文档生成工具之一,有以下几个原因:
- Swagger 可以生成一个具有互动性的API控制台,开发者可以用来快速学习和尝试API。
- Swagger 可以生成客户端SDK代码用于各种不同的平台上的实现。
- Swagger 文件可以在许多不同的平台上从代码注释中自动生成。
- Swagger 有一个强大的社区,里面有许多强悍的贡献者。
初次整合
在初次使用swagger时确实体会到了其方便快捷的对文档处理特有的优势,虽然有点对其页面展示方式有点无力吐槽,但是对于后端开发来说这无疑比那些手动去写接口文档所带来的便捷是无与伦比的。
在整合spingboot时几乎没有任何问题,方便简洁几句注解就可以解决restful风格的接口文档处理问题。
不得不提在此之前,我也在项目中自己写过一个基于注释去解析的API接口文档,无疑那是简陋的。起初那只是为jfinal写的一个接口文档,当然前端页面很漂亮用的是react去写的,源代码解析也是自己去着手编写的。但是当接触到springboot之后,接触到restful风格接口之后,确实捉襟见肘了,现在也懒得去改那些接口解析让它去支持这些风格,因为那样将会付出不少时间,于是选择了swagger。
好了不多说,直接上手说说哪些在使用swagger时遇到的问题。
springcloud zuul多项目整合统一swagger问题。
在整合springcloud问题时,由于多项目时怎么去统一swagger文档确实是问题所在,当初也是翻遍了好多网上大神的经验之后得出的一个方案。此处直接上代码,只需要一段小小的配置即可实现。
@Component
@Primary
public class DocumentationConfig implements SwaggerResourcesProvider {
@Override
public List 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;
}
}
Swagger + oauth2.0 + jwt授权时,接口访问问题处理
接下来最重要的环节,如何解决jwt授权时问题,网上给出的解决方案中,突出的一个是在统一的方法上去添加请求头 header。但这无疑是很麻烦的一件事,不可能因为涉及到授权问题,每个方法调用时都要去添加token吧。
经过google又结合了大神给出的新资讯终于找到了另一个方案,特此贴上代码。
代码摘录:https://cloud.tencent.com/developer/article/1335430
@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 securitySchemes() {
List apiKeyList= new ArrayList();
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
return apiKeyList;
}
private List securityContexts() {
List securityContexts=new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build());
return securityContexts;
}
List defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List 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();
}
}
fegin 服务间调用,token失效问题
在服务与服务之间调用时,遇到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 服务间调用优化,异步线程调用,token失效问题
当多线程异步调用的情况下,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));
}
}