这是我参与「第五届青训营 」笔记创作活动的第7天。
最近在做青训营的大项目,需要进行用户验证,于是稍微去了解了一下Hertz框架里跟认证相关的中间件。
要使用该中间件,需要导入Hertz的keyauth包。先看看该中间件的执行流程
func(c context.Context, ctx *app.RequestContext) {
// Filter request to skip middleware
if cfg.filterHandler != nil && cfg.filterHandler(c, ctx) {
ctx.Next(c)
return
}
// Extract and verify key
key, err := extractor(ctx)
if err != nil {
cfg.errorHandler(c, ctx, err)
return
}
valid, err := cfg.validator(c, ctx, key)
if err == nil && valid {
ctx.Set(cfg.contextKey, key)
cfg.successHandler(c, ctx)
return
}
cfg.errorHandler(c, ctx, err)
}
其中filterHandler、extractor、errorHandler、validator、successHandler都是我们可以自定义的函数。例如可以使用keyauth.New(keyauth.WithValidator(validator))的方式创建自己的中间件。
首先,该中间件判断是否需要对该请求进行认证。若filterHandler则调用之,返回值若为true则跳过认证步骤。
然后从请求中提取认证所需要的字段。可以指定从cookie、header、param、query、form中提取,提取失败会返回ErrMissingOrMalformedAPIKey错误。
若提取失败,则调用errorHandler函数进行错误处理。默认情况下(即不使用自定义的errorHandler),若字段提取失败,返回http.StatusBadRequest,否则返回http.StatusUnauthorized。
之后调用validator,刚才提取出的字段会被作为参数传入,用于判断是否认证通过。这个函数是认证的关键所在,可以使用刚才提取出的用户名和密码去数据库查询,也可以用提取出的token去redis中匹配用户id等。但是如果采用jwt认证,Hertz有专门的jwt中间件。
若认证成功,调用successHandler进行后续处理,默认什么也不做,直接调用ctx.Next。
若认证失败,则调用errorHandler。
如果没有什么特殊需求,那么只需要自定义需要提取的参数,以及认证函数即可,例如:
keyauth.New(
keyauth.WithKeyLookUp("query:token", "Bearer"),
keyauth.WithValidator(func(ctx context.Context, c *app.RequestContext, s string) (bool, error) {
claim, err := validateToken(s)
if err != nil {
return false, nil
}
if claim.ExpiresAt < time.Now().Unix() {
return false, nil
}
c.Set("uid", claim.Id)
return true, nil
}),
)
该中间件从到来的请求中的query获取token值,然后在validator中解析token,判断其是否过期。若未过期,则将其中的用户ID存入上下文中,以便后续调用链上的函数获取。