神器 SpringDoc 横空出世!最适合 SpringBoot 的API文档工具来了

神器 SpringDoc 横空出世!最适合 SpringBoot 的API文档工具来了,第1张

之前在SpringBoot项目中一直使用的是SpringFox提供的Swagger库,上了下官网发现已经有接近两年没出新版本了!前几天升级了SpringBoot 2.6.x 版本,发现这个库的兼容性也越来越不好了,有的常用注解属性被废弃了居然都没提供替代!无意中发现了另一款Swagger库SpringDoc,试用了一下非常不错,推荐给大家!

SpringDoc简介

SpringDoc是一款可以结合SpringBoot使用的API文档生成工具,基于OpenAPI 3,目前在Github上已有1.7K+Star,更新发版还是挺勤快的,是一款更好用的Swagger库!值得一提的是SpringDoc不仅支持Spring WebMvc项目,还可以支持Spring WebFlux项目,甚至Spring Rest和Spring Native项目,总之非常强大,下面是一张SpringDoc的架构图。

使用

接下来我们介绍下SpringDoc的使用,使用的是之前集成SpringFox的mall-tiny-swagger项目,我将把它改造成使用SpringDoc。

集成

首先我们得集成SpringDoc,在pom.xml中添加它的依赖即可,开箱即用,无需任何配置

<!--springdoc 官方Starter-->org.springdocspringdoc-openapi-ui1.6.6

从SpringFox迁移

我们先来看下经常使用的Swagger注解,看看SpringFox的和SpringDoc的有啥区别,毕竟对比已学过的技术能该快掌握新技术;

接下来我们对之前Controller中使用的注解进行改造,对照上表即可,之前在@Api注解中被废弃了好久又没有替代的description属性终于被支持了!

/**

* 品牌管理Controller

* Created by macro on 2019/4/19.

*/@Tag(name ="PmsBrandController", description ="商品品牌管理")@Controller@RequestMapping("/brand")publicclassPmsBrandController{@AutowiredprivatePmsBrandService brandServiceprivatestaticfinalLogger LOGGER = LoggerFactory.getLogger(PmsBrandController.class)@Operation(summary ="获取所有品牌列表",description ="需要登录后访问")@RequestMapping(value ="listAll", method = RequestMethod.GET)@ResponseBodypublicCommonResult>getBrandList() {returnCommonResult.success(brandService.listAllBrand())    }@Operation(summary ="添加品牌")@RequestMapping(value ="/create", method = RequestMethod.POST)@ResponseBody@PreAuthorize("hasRole('ADMIN')")publicCommonResult createBrand(@RequestBodyPmsBrand pmsBrand) {        CommonResult commonResult        int count = brandService.createBrand(pmsBrand)if(count ==1) {            commonResult = CommonResult.success(pmsBrand)            LOGGER.debug("createBrand success:{}", pmsBrand)        }else{            commonResult = CommonResult.failed(" *** 作失败")            LOGGER.debug("createBrand failed:{}", pmsBrand)        }returncommonResult    }@Operation(summary ="更新指定id品牌信息")@RequestMapping(value ="/update/{id}", method = RequestMethod.POST)@ResponseBody@PreAuthorize("hasRole('ADMIN')")publicCommonResult updateBrand(@PathVariable("id")Longid,@RequestBodyPmsBrand pmsBrandDto, BindingResult result) {        CommonResult commonResult        int count = brandService.updateBrand(id, pmsBrandDto)if(count ==1) {            commonResult = CommonResult.success(pmsBrandDto)            LOGGER.debug("updateBrand success:{}", pmsBrandDto)        }else{            commonResult = CommonResult.failed(" *** 作失败")            LOGGER.debug("updateBrand failed:{}", pmsBrandDto)        }returncommonResult    }@Operation(summary ="删除指定id的品牌")@RequestMapping(value ="/delete/{id}", method = RequestMethod.GET)@ResponseBody@PreAuthorize("hasRole('ADMIN')")publicCommonResult deleteBrand(@PathVariable("id")Longid) {        int count = brandService.deleteBrand(id)if(count ==1) {            LOGGER.debug("deleteBrand success :id={}", id)returnCommonResult.success(null)        }else{            LOGGER.debug("deleteBrand failed :id={}", id)returnCommonResult.failed(" *** 作失败")        }    }@Operation(summary ="分页查询品牌列表")@RequestMapping(value ="/list", method = RequestMethod.GET)@ResponseBody@PreAuthorize("hasRole('ADMIN')")publicCommonResult>listBrand(@RequestParam(value ="pageNum", defaultValue ="1")@Parameter(description ="页码")Integer pageNum,@RequestParam(value ="pageSize", defaultValue ="3")@Parameter(description ="每页数量")Integer pageSize) {        List brandList = brandService.listBrand(pageNum, pageSize)returnCommonResult.success(CommonPage.restPage(brandList))    }@Operation(summary ="获取指定id的品牌详情")@RequestMapping(value ="/{id}", method = RequestMethod.GET)@ResponseBody@PreAuthorize("hasRole('ADMIN')")publicCommonResult brand(@PathVariable("id")Longid) {returnCommonResult.success(brandService.getBrand(id))    }}

接下来进行SpringDoc的配置,使用OpenAPI来配置基础的文档信息,通过GroupedOpenApi配置分组的API文档,SpringDoc支持直接使用接口路径进行配置。

/**

* SpringDoc API文档相关配置

* Created by macro on 2022/3/4.

*/@ConfigurationpublicclassSpringDocConfig{@BeanpublicOpenAPImallTinyOpenAPI(){returnnewOpenAPI()                .info(newInfo().title("Mall-Tiny API")                        .description("SpringDoc API 演示")                        .version("v1.0.0")                        .license(newLicense().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))                .externalDocs(newExternalDocumentation()                        .description("SpringBoot实战电商项目mall(50K+Star)全套文档")                        .url("http://www.macrozheng.com"))    }@BeanpublicGroupedOpenApipublicApi(){returnGroupedOpenApi.builder()                .group("brand")                .pathsToMatch("/brand/**")                .build()    }@BeanpublicGroupedOpenApiadminApi(){returnGroupedOpenApi.builder()                .group("admin")                .pathsToMatch("/admin/**")                .build()    }}

结合SpringSecurity使用

由于我们的项目集成了SpringSecurity,需要通过JWT认证头进行访问,我们还需配置好SpringDoc的白名单路径,主要是Swagger的资源路径;

/**

* SpringSecurity的配置

* Created by macro on 2018/4/26.

*/@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf.disable().sessionManagement()// 基于token,所以不需要session.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers(HttpMethod.GET,// Swagger的资源路径需要允许访问"/","/swagger-ui.html","/swagger-ui/","/*.html","/favicon.ico","/**/*.html","/**/*.css","/**/*.js","/swagger-resources/**","/v3/api-docs/**").permitAll().antMatchers("/admin/login")// 对登录注册要允许匿名访问.permitAll().antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求.permitAll().anyRequest()// 除上面外的所有请求全部需要鉴权认证.authenticated()            }}

然后在OpenAPI对象中通过addSecurityItem方法和SecurityScheme对象,启用基于JWT的认证功能。

/**

* SpringDoc API文档相关配置

* Created by macro on 2022/3/4.

*/@ConfigurationpublicclassSpringDocConfig{privatestaticfinalString SECURITY_SCHEME_NAME ="BearerAuth"@BeanpublicOpenAPImallTinyOpenAPI(){returnnewOpenAPI()                .info(newInfo().title("Mall-Tiny API")                        .description("SpringDoc API 演示")                        .version("v1.0.0")                        .license(newLicense().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))                .externalDocs(newExternalDocumentation()                        .description("SpringBoot实战电商项目mall(50K+Star)全套文档")                        .url("http://www.macrozheng.com"))                .addSecurityItem(newSecurityRequirement().addList(SECURITY_SCHEME_NAME))                .components(newComponents()                                .addSecuritySchemes(SECURITY_SCHEME_NAME,newSecurityScheme()                                                .name(SECURITY_SCHEME_NAME)                                                .type(SecurityScheme.Type.HTTP)                                                .scheme("bearer")                                                .bearerFormat("JWT")))    }}

测试

接下来启动项目就可以访问Swagger界面了,访问地址:http://localhost:8088/swagger-ui.html

我们先通过登录接口进行登录,可以发现这个版本的Swagger返回结果是支持高亮显示的,版本明显比SpringFox来的新;

然后通过认证按钮输入获取到的认证头信息,注意这里不用加bearer前缀;

之后我们就可以愉快地访问需要登录认证的接口了;

看一眼请求参数的文档说明,还是熟悉的Swagger样式!

常用配置

SpringDoc还有一些常用的配置可以了解下,更多配置可以参考官方文档。

springdoc:swagger-ui:# 修改Swagger UI路径path:/swagger-ui.html# 开启Swagger UI界面enabled:trueapi-docs:# 修改api-docs路径path:/v3/api-docs# 开启api-docsenabled:true# 配置需要生成接口文档的扫描包packages-to-scan:com.macro.mall.tiny.controller# 配置需要生成接口文档的接口路径paths-to-match:/brand/**,/admin/**

总结

在SpringFox的Swagger库好久不出新版的情况下,迁移到SpringDoc确实是一个更好的选择。今天体验了一把SpringDoc,确实很好用,和之前熟悉的用法差不多,学习成本极低。而且SpringDoc能支持WebFlux之类的项目,功能也更加强大,使用SpringFox有点卡手的朋友可以迁移到它试试!

参考资料

项目地址:https://github.com/springdoc/springdoc-openapi

官方文档:https://springdoc.org/

项目源码地址

https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-springdoc

https://mp.weixin.qq.com/s/scityFhZp9BOJorZSdCG0w

软件开发中,安全是永恒的话题,FastAPI作为一个优秀的Python Web开发框架,为用户提供了多种工具,帮助用户以标准的方式轻松快速地解决软件开发中的安全性。

FastAPI 的 fastapi.security 模块中为各种安全方案提供了一些工具,这些工具简化了这些安全机制的使用方法。

FastAPI提供的OAuth2PasswordBearer是使用 OAuth2的密码授权模式的Bearer Token(不记名 token) 。创建OAuth2PasswordBearer 实例需要接收URL作为参数。

客户端会向该 URL 通过表单的格式发送 username 和 password 参数,然后得到一个 token 值;OAuth2PasswordBearer 并不会创建相应的 URL 路径 *** 作,只是指明了客户端用来获取 token 的目标 URL。

代码示例:

在上面的代码中, tokenUrl="token"指的token是相对 URL 。

此时访问,其返回结果:

上面的结果表明:访问的内容以及被保护,必须经过授权后才可以访问。

当获取到表单数据后,需要进行密码校验,一般情况下,我们都会考虑使用哈希密码,PassLib 是一个用于处理哈希密码的非常好的 Python 包,它支持许多安全哈希算法以及配合算法使用的实用程序。

具体passlib的使用方法可以查看其文档https://passlib.readthedocs.io/en/stable

下面的代码示例在上面代码的基础上增加用户登录及Token验证

启动应用并执行请求:

测试无效登录:

测试正常登录:

返回token,在Headers中使用token访问:

修改token后请求:

上面的代码如果去掉 await verify_token(token) 行,则:

curl -H "Authorization:Bearer u000010007" http://127.0.0.1:8000 -i

会得到返回结果,原因是默认情况下,OAuth2PasswordBearer只负责请求头中是否具有Authorization:Bearer,如果有就会执行相应的请求,所以,为了验证Token的正确性,需要每个方法都执行相应的验证代码。

本例只作为例子,在实际开发中不会直接拿用户ID作为Token,为了提高系统的安全性,需要使用 JWT。下面我们就介绍 JWT。

JWT是一个将 JSON 对象编码为密集且没有空格的长字符串的标准。 具体学习和了解 JWT,请参考 https://jwt.io。

需要提到的主要是 JWT中的sub,JWT 的规范中有一个 sub 键,值为该令牌的主题。使用它并不是必须的,但这是我们放置用户标识的地方,所以一般情况下,我们在sub中存放用户ID, 为了避免 ID 冲突,当为创建 JWT 令牌时,可以在 sub 键的值前加上前缀,例如 username:、userid:等。

在 Python 中生成和校验 JWT 令牌 ,可以使用PyJWT,也可以使用 python-jose 。我们在本例中使用 python-jose 来编写代码。

使用:

使用 JWT,需要在系统中添加一个SECRET_KEY变量,用于生成令牌,如:

以下代码在上面代码的基础上使用 JWT 令牌。

与前面的代码差别之处:

1.生成Token的函数:build_access_token

2.校验Token的函数:verify_token

3.登录函数:login

请求测试:

登录:

令牌访问:

错误的令牌访问:

在大部分应用程序中,当用户访问某个接口API的时候,都需要明确访问者的身份,所以在应用程序中需要随时获取当前用户,由于在 JWT 令牌的 sub 字段中已经保存了用户信息,所以获取当前用户只需要对令牌解码即可。

在上面的代码的基础上,增加两个函数,代码如下:

请求测试:

以上,我们完成了一个简单的安全性示例,FastAPI提供的安全性框架帮助我们节约了很多代码,但在实际开发中,我们常常使用微服务的方式来开发,对于鉴权最好设计独立的微服务进行处理。后面我们会展示一个采用FastAPI开发的鉴权微服务,以便在此基础上进行业务系统的开发。

一种令牌类型。Oauth2.0的官网有说明(OAuth 2.0 Bearer Token Usage)

RFC6749

RFC6750

Bearer Type Access Token:

BEARER类型的token是在RFC6750中定义的一种token类型,OAuth2.0协议RFC6749对其也有所提及,算是对RFC6749的一个补充。BEARER类型token是建立在HTTP/1.1版本之上的token类型,需要TLS(Transport Layer Security)提供安全支持,该协议主要规定了BEARER类型token的客户端请求和服务端验证的具体细节。

MAC Type Access Token:

前面介绍了BEARER类型的token,RFC6750明确说明该类型token需要TLS(Transport Layer Security)提供安全支持。虽然现今大部分站点都已经或正在由HTTP向HTTPS迁移,但是仍然会有站点继续在使用HTTP,在这类站点中BEARER类型的token存在安全隐患,这个时候MAC类型的token正是用武之地,MAC类型的token设计的主要目的就是为了应对不可靠的网络环境。

  MAC类型相对于BEARER类型对于用户资源请求的区别在于,BEARER类型只需要携带授权服务器下发的token即可,而对于MAC类型来说,除了携带授权服务器下发的token,客户端还要携带时间戳,nonce,以及在客户端计算得到的mac值等信息,并通过这些额外的信息来保证传输的可靠性。

其实很多API服务并没有使用https加密连接(却使用了BEARER类型的token),所以你可以利用一些抓包工具进行抓包分析,可以看到一些应用请求附带的token,你拿这个token也可以访问相应的第三方api,可能有的token时效比较短,用不了多久就会过期。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11712793.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-18
下一篇 2023-05-18

发表评论

登录后才能评论

评论列表(0条)

保存