go zero 中间件的使用
一、中间件介绍
中间件(Middleware)是一个在请求和响应处理之间插入的程序或者函数,它可以用来处理、修改或者监控 HTTP 请求和响应的各个方面。中间件通常位于 web 服务器和应用逻辑之间,它们可以拦截请求和响应流,对其进行某种处理,然后决定是否将控制传递给下一个中间件或最终的处理程序。
1.中间件的核心概念
-
请求拦截:中间件能够在请求到达目标处理器之前,对请求进行分析、修改或记录。
-
响应拦截:同样,中间件也可以在响应返回给客户端之前,对响应进行处理,例如添加 headers、压缩响应体、格式化响应数据等。
-
链式调用:多个中间件可以串联在一起, 形成一个中间件链,依次处理请求和响应。
2.中间件的用途
中间件可以用于以下多个方面:
- 日志记录:记录 HTTP 请求和响应的详细信息,例如请求路径、方法、状态码等。这有助于进行审计和调试。
- 认证和授权:验证请求是否具有必要的权限,确保用户或系统的身份是否合法。这对于保护敏感接口尤其重要。
- 请求限流:控制访问速率,避免服务被恶意请求或流量洪水淹没。这可以提高系统的稳定性和可用性。
- 跨域资源共享 (CORS):处理浏览器的跨域请求。中间件可以在响应中添加适当的 CORS 头,允许来自不同源的请求。
- 错误处理:捕获处理过程中的错误(如 panic)并生成适当的 HTTP 响应。这样可以确保系统在错误情况下不会崩溃,并能够正常返回相应。
- 数据格式化:统一请求和响应的数据格式,例如将响应数据转换为 JSON 格式,或者对请求中的参数进行验证和转换。
- 国际化 (i18n):处理用户的语言偏好和内容的本地化,使应用能够支持多语言。
3.中间件的工作流程
一般来说,工作流程如下:
- 用户发起请求,请求到达 Web 服务器。
- 中间件链开始处理:
- 第一个中间件接收请求,进行相应的处理(如日志记录)。
- 控制权传递给下一个中间件。
- 这个过程可以一直持续,直到所有中间件处理完请求。
- 最终请求到达目标处理器,处理器生成响应。
- 响应同样会经过中间件链进行处理(如添加 headers)。
- 最终响应返回给用户。
这种结构类似于"洋葱模型":请求像是从外到内穿过洋葱层,而响应则是从内到外穿出。
二、go zero内置中间件介绍
go zero 提供了一系列内置的中间件,以帮助开发者管理 HTTP 请求的不同方面。
还记得之前的jwt鉴权吗?那边我们只实现了token生成,但是并没有实现验证,但是go zero能够自动解析token,就是因为它内置了AuthorizeHandler(鉴权管理中间件)
。
1. 内置中间件一览
在 go zero 中内置了如下中间件:
中间件名称 | 功能描述 | 默认状态 |
---|---|---|
AuthorizeHandler | 身份验证与授权控制 | 启用 |
BreakerHandler | 服务熔断,防止级联故障 | 启用 |
LogHandler | 请求日志记录 | 启用 |
MetricHandler | 服务指标统计 | 启用 |
PrometheusHandler | Prometheus 监控指标收集 | 启用 |
RecoverHandler | Panic 恢复,防止服务崩溃 | 启用 |
TimeoutHandler | 请求超时控制 | 启用 |
TraceHandler | 分布式链路追踪 | 启用 |
MaxConnsHandler | 最大连接数限制 | 启用 |
SheddingHandler | 负载均衡与请求分流 | 启用 |
GunzipHandler | 响应压缩管理 | 启用 |
MaxBytesHandler | 请求体大小限制 | 启用 |
ContentSecurityHandler | 内容安全策略 | 启用 |
CryptionHandler | 请求/响应加解密 | 启用 |
这些中间件的具体实现可以去看github.com\zeromicro\go [email protected]\rest\handler
目录的内容。
2. 核心中间件详解
认证中间件 (AuthorizeHandler)
JWT 认证是 Go-Zero 中最常用的身份验证方式,由内置的 AuthorizeHandler 提供支持。
工作原理:
- 验证请求头中的 Authorization 字段
- 解析 JWT Token 并验证有效性
- 将用户信息存入上下文,供后续处理器使用
源码解析:
// Authorize 函数用于创建一个 HTTP 中间件,该中间件可对传入的 HTTP 请求进行身份验证。
// 它会从请求中解析 JWT(JSON Web Token),验证其有效性,并将 JWT 中的自定义声明添加到请求的上下文(context)中,供后续处理程序使用。
// 参数 secret 是用于验证 JWT 的密钥,为字符串类型。
// 参数 opts 是可变参数,类型为 AuthorizeOption,用于配置身份验证的选项。
// 函数返回一个 func(http.Handler) http.Handler 类型的函数,即一个 HTTP 中间件。
func Authorize(secret string, opts ...AuthorizeOption) func(http.Handler) http.Handler {
// 定义一个 AuthorizeOptions 类型的变量 authOpts,用于存储身份验证的选项。
var authOpts AuthorizeOptions
// 遍历传入的选项 opts
for _, opt := range opts {
// 调用每个选项函数,将 authOpts 的指针传递给它们,以设置具体的选项。
opt(&authOpts)
}
// 创建一个 token.TokenParser 类型的实例 parser,用于解析 JWT。
parser := token.NewTokenParser()
// 返回一个中间件函数,该函数接受一个 http.Handler 类型的参数 next,表示下一个处理程序。
return func(next http.Handler) http.Handler {
// 返回一个 http.HandlerFunc 类型的处理程序,用于处理传入的 HTTP 请求。
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 调用 parser 的 ParseToken 方法,从请求 r 中解析 JWT,同时传入当前密钥 secret 和之前的密钥 authOpts.PrevSecret。
// tok 是解析出的 JWT 对象,err 是可能出现的错误。
tok, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
if err != nil {
// 如果解析过程中出现错误,说明 token 无效或解析失败。
// 调用 unauthorized 函数返回未授权错误,并终止请求处理。
unauthorized(w, r, err, authOpts.Callback)
return
}
// 检查解析出的 JWT 是否有效。
if !tok.Valid {
// 如果 JWT 无效,调用 unauthorized 函数返回未授权错误,并终止请求处理。
unauthorized(w, r, errInvalidToken, authOpts.Callback)
return
}
// 尝试将 JWT 的声明(Claims)转换为 jwt.MapClaims 类型。
// claims 是转换后的声明对象,ok 表示转换是否成功。
claims, ok := tok.Claims.(jwt.MapClaims)
if !ok {
// 如果转换失败,说明 JWT 中没有有效的声明。
// 调用 unauthorized 函数返回未授权错误,并终止请求处理。
// errNoClaims 是一个预定义的错误对象,表示没有声明。
unauthorized(w, r, errNoClaims, authOpts.Callback)
return
}
// 获取请求的上下文 ctx。
ctx := r.Context()
// 遍历 JWT 的声明,将自定义声明添加到上下文中。
for k, v := range claims {
switch k {
// 忽略标准的 JWT 声明,如受众、过期时间、ID 等。
case jwtAudience, jwtExpire, jwtId, jwtIssueAt, jwtIssuer, jwtNotBefore, jwtSubject:
// 忽略标准的声明,不做处理
default:
// 将自定义声明添加到上下文中,键为 k,值为 v。
ctx = context.WithValue(ctx, k, v)
}
}
// 将带有更新后的上下文的请求传递给下一个处理程序 next 进行处理。
next.ServeHTTP(w, r