
开发工具代码生成API设计【免费下载链接】oapi-codegenGenerate Go client and server boilerplate from OpenAPI 3 specifications项目地址https://gitcode.com/gh_mirrors/oa/oapi-codegen点击查看免费下载本篇技术指南以 oapi-codegen 仓库中的 Authenticated API 示例 为核心完整讲解如何用 OpenAPI 3 规范声明 Bearer JWT 安全方案、通过things:w这类自定义 Scope 实现按路径per-path的权限校验并在生成的 Go 服务中接入 kin-openapi 请求校验中间件与 lestrrat-go/jwx JWT 库。读完本文你将掌握从规范定义、代码生成到运行时验证与单元测试的完整 JWT 鉴权落地路径并可直接复制该示例中的鉴权代码到自己的项目中使用。示例概览一个需要登录的 Thing 管理服务该示例实现了一个极其简单的“Thing 管理”服务器允许创建POST /things和列出GET /thingsThing对象。它的核心价值不在业务逻辑而在于演示如何在一个 oapi-codegen 生成的 Echo/标准库服务器上让 JWT 鉴权与 OpenAPI 的 Scope 声明联动工作。据 README 说明这套代码借鉴了作者在 DeepMap 生产环境中的用法作者也明确提示部分代码未来可以泛化当前阶段直接 copy/paste 并按需修改是最佳使用方式。完整 API 规范见 api.yaml其中定义了三个 schemaThing仅含必填的name字符串字段ThingWithID通过allOf组合Thing与新增的必填idint64字段Errorcodeint32与message两个必填字段用于统一错误响应。对应的生成代码位于 echo/api/api.gen.go由 config.yaml 配置生成——该配置开启了echo-server、client、models与embedded-spec将规范内嵌到生成的GetSpec()中供运行时校验中间件使用。规范层全局 BearerAuth 与按操作的 Scope 覆盖定义 JWT 安全方案在#components/securitySchemes中定义名为BearerAuth的 HTTP Bearer 安全方案声明其 token 格式为 JWTBearerAuth: type: http scheme: bearer bearerFormat: JWT全局强制所有端点鉴权通过顶层security字段将BearerAuth声明为所有端点的全局安全要求security: - BearerAuth: [ ]这里[ ]表示空 Scope 列表意味着所有 API 端点都要求携带合法的 JWT Bearer token但不要求任何特定 Scope。用局部 security 覆盖全局声明写权限仅持有合法 JWT 还不足以执行写操作。示例约定了一种 Scope 命名惯例名词 冒号 访问类型例如things:w表示对things的写权限:w即 write而读权限read隐含在“持有合法 JWT”这一前提中。为了让addThing操作需要things:w权限在POST /things上覆盖全局 securitysecurity: - BearerAuth: - things:w注意api.yaml中这段局部 security 与listThingsGET /things形成对照listThings不覆盖全局声明因此只需合法 JWTaddThing覆盖后多了一层 Scope 要求。这就是“按路径校验 Scope”的核心机制——OpenAPI 规范本身描述了权限要求运行时校验由中间件落地。实现层让 kin-openapi 中间件替你完成鉴权鉴权是安全敏感逻辑示例刻意避免自己从零实现而是复用两个成熟库lestrrat-go/jwx负责 JWT/JWS 的签名与验证kin-openapiopenapi3filter 包负责请求校验包括把规范中的 security 要求翻译成一次AuthenticationFunc调用。在 echo/server/server.go 的CreateMiddleware中通过OapiRequestValidatorWithOptions把自定义的AuthenticationFunc注入校验器validator : middleware.OapiRequestValidatorWithOptions(spec, middleware.Options{ Options: openapi3filter.Options{ AuthenticationFunc: NewAuthenticator(v), }, })这里middleware是github.com/oapi-codegen/echo-middlewarespec来自生成代码中的api.GetSpec()即embedded-spec: true的产物。请求到达时openapi3filter会调用AuthenticationFunc判定请求是否通过鉴权校验失败时请求以403 Forbidden{message: Security requirements failed}终止不会进入业务 handler。Authenticate鉴权主流程echo/server/jwt_authenticator.go 中的Authenticate函数是整条链路的核心它按顺序完成四件事核对安全方案名称要求input.SecuritySchemeName BearerAuth否则直接报错从请求头提取 JWSGetJWSFromRequest读取Authorization头要求格式为Bearer token严格带一个空格头缺失返回ErrNoAuthHeader格式错误返回ErrInvalidAuthHeader验证签名调用JWSValidator.ValidateJWS(jws)拿到解析后的jwt.Token核对 Scope调用CheckTokenClaims(input.Scopes, token)确认 token 中的权限声明覆盖了该操作要求的全部 Scopeinput.Scopes正是 openapi3filter 从规范中解析出的things:w等要求。func Authenticate(v JWSValidator, ctx context.Context, input *openapi3filter.AuthenticationInput) error { if input.SecuritySchemeName ! BearerAuth { return fmt.Errorf(security scheme %s ! BearerAuth, input.SecuritySchemeName) } jws, err : GetJWSFromRequest(input.RequestValidationInput.Request) if err ! nil { return fmt.Errorf(getting jws: %w, err) } token, err : v.ValidateJWS(jws) if err ! nil { return fmt.Errorf(validating JWS: %w, err) } err CheckTokenClaims(input.Scopes, token) if err ! nil { return fmt.Errorf(token claims dont match: %w, err) } // 将解析出的 token 放入 Echo context供 handler 读取 claims eCtx : middleware.GetEchoContext(ctx) eCtx.Set(JWTClaimsContextKey, token) return nil }一个值得注意的细节README 中描述权限存放于名为perms的 claim而示例源码 echo/server/fake_jws.go 中实际定义的常量是const PermissionsClaim perm即 JWT 载荷里的键名为perm取 permissions 之意以缩短 token。若你基于此示例改造务必以源码为准统一 claim 名。CheckTokenClaimsScope 全量包含校验CheckTokenClaims先把 token 中声明的权限列表转成 map 以便 O(1) 查找再遍历expectedClaimsopenapi3filter 传入的 Scope 要求只要有一个缺失就返回ErrClaimsInvalidfor _, e : range expectedClaims { if !claimsMap[e] { return ErrClaimsInvalid } } return nil而GetClaimsFromToken对“token 中没有permclaim”的处理是宽容的返回空列表而非报错——因为此时 token 已通过签名验证只是没有任何权限而已这正好对应 README 中 Reader token 的场景。若perm存在但列表中的某个元素不是字符串则会返回明确的错误。三大组件的设计从“签发”到“验证”的闭环README 明确指出示例由三部分组成逐一拆解如下。1) FakeAuthenticator本地签发 JWT 的假身份提供方echo/server/fake_jws.go 中的FakeAuthenticator是一个内置了 ECDSA 私钥的“伪 IdP”既能签发 JWT又能校验自己签发的 token。它满足JWSValidator接口ValidateJWS(jws string) (jwt.Token, error)并提供了三个能力加载私钥NewFakeAuthenticator通过ecdsafile.LoadEcdsaPrivateKey见 pkg/ecdsafile/ecdsafile.go加载硬编码的 P-256 私钥并把公钥包装成带ES256算法与fake-key-id的 JWK加入 KeySet 供验证使用。该私钥由openssl ecparam -name prime256v1 -genkey -noout -out ecprivatekey.pem生成签发 JWTCreateJWSWithClaims(claims)创建 JWT设置issfake-issuer、audexample-users与permclaim再用 ES256 签名返回 JWS头部含typJWT、kidfake-key-id验证 JWTValidateJWS用jwt.Parse并限定jwt.WithKeySet(f.KeySet)、jwt.WithAudience(FakeAudience)、jwt.WithIssuer(FakeIssuer)——即同时校验签名、受众与签发者三类关键声明。示例中的私钥常量直接躺在源码里README 对此特别强调真实应用中绝不能在代码里存放密钥材料生产环境应接入 Google、Auth0、AWS Cognito 等身份提供商通过授权协议发放 JWT应用侧只保留公钥用于验证。2) JWT 签名验证与真实 IdP 对接的通用范式ValidateJWS这段逻辑“足够严谨可直接用作生产代码的范例”README 原话。它展示了接入任意 IdP 时的标准姿势把 IdP 的公钥集合JWK Set配给jwt.Parse并声明 issuer 与 audience 白名单从而拒绝任何“签名合法但来源不对”的 token。在真实场景中这个 KeySet 通常来自 IdP 的 JWKS 端点而示例里则是 FakeAuthenticator 自产自销的 KeySet。3) Claims 校验JWT 是自由格式校验规则由你定义JWT 的 payload 是高度自由的结构放什么、怎么解释完全取决于实现。本示例约定permclaim 是字符串数组存放被授予的权限如things:w而Authenticate负责把它与 openapi3filter 传入的 Scope 要求做包含比对。这套约定正是 README 所说的“scope 命名惯例名词 访问类型”。端到端运行验证Reader 与 Writer 两枚 Token 的权限实验启动服务并获取 Token以 Echo 版本为例在仓库根目录运行$ go run ./examples/authenticated-api/echo/main.goecho/main.go 在启动时用CreateJWSWithClaims生成两枚 token 并打印到日志Reader tokenCreateJWSWithClaims([]string{})无任何 ScopeWriter tokenCreateJWSWithClaims([]string{things:w})含写权限。README 记录了当年的实际运行输出服务监听0.0.0.0:8080可用-port参数覆盖端口2021/10/07 14:32:45 Reader token eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOltdfQ.Hf9dCNJLa0HQfbtJi7ndASbkTfrLc6bZBJK8HaPqtzXiDkTH6sMRoiNhf6Kb1g6z3R1tN3XEpXsghxlMRO3OLA 2021/10/07 14:32:45 Writer token eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOlsidGhpbmdzOnciXX0.CbPT1hzWmyTt0lTyv-fiyUlnY1SGa0vrX52yFjeigx2PA1-78LVH0z5hukPKkLMPDMXL9AJrtNp0elWSD_qrBw把两枚 token 存入环境变量方便后续使用实际运行时请以你终端打印出的 token 为准export RJWTeyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOltdfQ.Hf9dCNJLa0HQfbtJi7ndASbkTfrLc6bZBJK8HaPqtzXiDkTH6sMRoiNhf6Kb1g6z3R1tN3XEpXsghxlMRO3OLA export WJWTeyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOlsidGhpbmdzOnciXX0.CbPT1hzWmyTt0lTyv-fiyUlnY1SGa0vrX52yFjeigx2PA1-78LVH0z5hukPKkLMPDMXL9AJrtNp0elWSD_qrBw行为矩阵四种请求 × 两枚 TokenREADME 用 HTTPie 演示了全部鉴权行为curl亦可HTTPie 在 shell 中更易用无凭证请求一律 403$ http http://localhost:8080/things HTTP/1.1 403 Forbidden Content-Type: application/json; charsetUTF-8 { message: Security requirements failed } $ http POST http://localhost:8080/things nameSomeThing HTTP/1.1 403 Forbidden Content-Type: application/json; charsetUTF-8 { message: Security requirements failed }Writer token 可以创建 Thing201$ http POST http://localhost:8080/things nameSomeThing Authorization:Bearer $WJWT HTTP/1.1 201 Created Content-Type: application/json; charsetUTF-8 { id: 0, name: SomeThing }Reader token 无法创建 Thing403$ http POST http://localhost:8080/things nameSomeThing2 Authorization:Bearer $RJWT HTTP/1.1 403 Forbidden Content-Type: application/json; charsetUTF-8 { message: Security requirements failed }两枚 token 都能列出 Things200$ http http://localhost:8080/things Authorization:Bearer $RJWT HTTP/1.1 200 OK Content-Type: application/json; charsetUTF-8 [ { id: 0, name: SomeThing } ] $ http http://localhost:8080/things Authorization:Bearer $WJWT HTTP/1.1 200 OK Content-Type: application/json; charsetUTF-8 [ { id: 0, name: SomeThing } ]实验结果与规范声明完全一致POST /things因局部 security 覆盖而要求things:wGET /things因沿用全局 security 而只要求合法 JWT。这正是“读权限隐式、写权限显式”的落地效果。单元测试把鉴权行为固化进 CI示例同时提供了完整的单元测试 echo/server/server_test.go它用与生产相同的装配方式NewServerCreateMiddlewareRegisterHandlers启动内存服务器并用oapi-codegen/testutil构造请求断言了 README 中的全部四条行为无凭证GET /things返回403Writer tokenPOST /thingsJSON body{name:Thing 1}返回201Reader tokenPOST /things返回403两枚 tokenGET /things均返回200。这套测试的价值在于鉴权规则不是靠手工 curl 验证的一次性行为而是可回归的测试资产。当你调整 Scope 命名或校验逻辑时运行go test ./examples/authenticated-api/echo/...即可立刻发现破坏。标准库变体同一套鉴权逻辑的 net/http 移植鉴权核心jwt_authenticator.go在两个变体中几乎逐字一致区别只在 Web 框架适配层Echo 版echo/server中间件类型为[]echo.MiddlewareFunc鉴权通过middleware.GetEchoContext(ctx)把jwt.Token写入echo.Context标准库版stdhttp/server中间件类型为func(next http.Handler) http.Handler使用github.com/oapi-codegen/nethttp-middlewarehandler 签名是标准的http.HandlerFunc风格ListThings(w http.ResponseWriter, r *http.Request)其鉴权代码中暂未写入 claims 到 context源码留有 TODO 注释。两者共享同一份 api.yaml仅生成配置不同Echo 版在 echo/api/config.yaml 中设置generate.echo-server: true标准库版在 stdhttp/api/config.yaml 中设置generate.std-http-server: true其余选项client、models、embedded-spec、skip-prune一致。标准库版同样可运行go run ./examples/authenticated-api/stdhttp/main.go。实战要点与生产化建议综合 README 的说明与源码实现落地这套鉴权方案时有几点值得注意信任边界只到签名验证为止JWT 本身不解决身份问题的全部ValidateJWS中必须同时校验签名、issuer 与 audience 白名单缺一不可密钥永不入代码示例的硬编码私钥仅用于演示生产环境用 IdP 的 JWKS 公钥集合替换jwk.Set私钥永远留在 IdP 侧Scope 命名与语义约定要统一things:w的“名词冒号动作”风格需要全团队遵守并在规范注释中写明读写语义同时注意 README 描述perms与源码常量perm的差异改造时以 fake_jws.go 中的PermissionsClaim为准局部 security 是覆盖而非叠加OpenAPI 规定操作级security会整体替换全局声明若某操作既要有things:w又要保留其他要求必须在局部声明中完整列出把鉴权测试写进 CI参考 server_test.go 的矩阵式断言防止 Scope 规则悄然回退。至此从api.yaml的securitySchemes与security声明到OapiRequestValidatorWithOptions注入AuthenticationFunc再到ValidateJWS与CheckTokenClaims的层层校验最后以运行时实验与单元测试双重验证——一套完整的“OpenAPI 描述权限 生成代码 运行时强制”的 JWT 鉴权方案已经闭环。以该示例为起点你只需替换 IdP、调整 claim 结构即可把同样的模式复用到自己的生产服务中。赞分享开发工具代码生成API设计【免费下载链接】oapi-codegenGenerate Go client and server boilerplate from OpenAPI 3 specifications项目地址https://gitcode.com/gh_mirrors/oa/oapi-codegen点击查看免费下载相关推荐oapi-codegen 流式 API 实战用 OpenAPI 3 生成 JSONL/SSE 服务端与客户端oapi codegen 流式 API 实战用 OpenAPI 3 生成 JSONL/SSE 服务端与客户端 本篇技术指南聚焦 oapi codegen 仓库开发工具代码生成API设计Mongoose JWT Bearer 认证实战基于 HS256 与 ES256 的嵌入式 HTTP 服务鉴权Mongoose JWT Bearer 认证实战基于 HS256 与 ES256 的嵌入式 HTTP 服务鉴权 本教程以 Mongoose 仓库中的 JWT嵌入式网络通信物联网go-swagger 实战基于 OAuth2 AccessCode 工作流Google OpenID的服务端鉴权go swagger 实战基于 OAuth2 AccessCode 工作流Google OpenID的服务端鉴权 本文以 go swagger 仓库官方教代码生成开发工具后端API设计上一篇volatility插件生态系统推荐15个必备第三方插件下一篇KoboldCpp 本地 AI 实战指南一个文件、零安装5 分钟跑通第一个 GGUF 模型创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考