缓存 auth0 JWK 是个坏主意吗
Is it a bad idea to cache auth0 JWK
我正在使用 auth0,我有两个客户端(ios、react)和一个使用 go-auth0 的 Go 后端 API。
我按照文档做了一个 Verify
方法,如下所示:
func Verify(handle httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
auth0Domain := viper.GetString("auth0.issuer")
audience := []string{viper.GetString("auth0.audience")}
client := auth0.NewJWKClient(auth0.JWKClientOptions{URI: auth0Domain + ".well-known/jwks.json"}, nil)
configuration := auth0.NewConfiguration(client, audience, auth0Domain, jose.RS256)
validator := auth0.NewValidator(configuration, nil)
_, err := validator.ValidateRequest(r)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
return
}
handle(w, r, p)
}
}
不幸的是,我注意到第一次验证需要大约 400 毫秒,随后的验证需要大约 50 毫秒。
但是,如果我用验证器的字段初始化结构,将所有设置代码移动到 Initialize()
,那么它只需要 ~1ms:
func Verify(handle httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
_, err := a.validator.ValidateRequest(r)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
return
}
handle(w, r, p)
}
}
这样做是不是一个坏主意?我今天刚刚了解 JWK 并查看 auth0 代码,他们似乎确实构建了一个缓存,但我并不完全理解它是如何工作的。
有人可以告诉我将配置移动到结构中并使用其验证器是否是个好主意吗?
更新
auth0 有一个内置方法可以做到这一点!这是一个例子:
auth0.NewJWKClientWithCache(auth0.JWKClientOptions{URI: a.issuer + ".well-known/jwks.json"}, nil, auth0.NewMemoryKeyCacher(time.Duration(10)*time.Second, 5))
使用此方法,它会为您缓存! :)
缓存客户端对象几乎肯定是安全的,通常这样做是个好主意。 (“创建一个客户端并重用它”是一个很好的通用规则。)
我的理解是 JWT 的签名密钥通常有效期为数月甚至更长。 (Auth0's documentation notes that its JWKS documents only ever have a single key, but it will issue signed tokens all the time, so the keys must be valid for "a while".) RFC 7517 没有在 JWKS 或单个 JWK 上定义任何与过期相关的参数,我认为最好的做法是在 JWKS 端点上使用普通的 HTTP 缓存控件来偶尔刷新它,但不要那么频繁.
我正在使用 auth0,我有两个客户端(ios、react)和一个使用 go-auth0 的 Go 后端 API。
我按照文档做了一个 Verify
方法,如下所示:
func Verify(handle httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
auth0Domain := viper.GetString("auth0.issuer")
audience := []string{viper.GetString("auth0.audience")}
client := auth0.NewJWKClient(auth0.JWKClientOptions{URI: auth0Domain + ".well-known/jwks.json"}, nil)
configuration := auth0.NewConfiguration(client, audience, auth0Domain, jose.RS256)
validator := auth0.NewValidator(configuration, nil)
_, err := validator.ValidateRequest(r)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
return
}
handle(w, r, p)
}
}
不幸的是,我注意到第一次验证需要大约 400 毫秒,随后的验证需要大约 50 毫秒。
但是,如果我用验证器的字段初始化结构,将所有设置代码移动到 Initialize()
,那么它只需要 ~1ms:
func Verify(handle httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
_, err := a.validator.ValidateRequest(r)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "Unauthorized"})
return
}
handle(w, r, p)
}
}
这样做是不是一个坏主意?我今天刚刚了解 JWK 并查看 auth0 代码,他们似乎确实构建了一个缓存,但我并不完全理解它是如何工作的。
有人可以告诉我将配置移动到结构中并使用其验证器是否是个好主意吗?
更新
auth0 有一个内置方法可以做到这一点!这是一个例子:
auth0.NewJWKClientWithCache(auth0.JWKClientOptions{URI: a.issuer + ".well-known/jwks.json"}, nil, auth0.NewMemoryKeyCacher(time.Duration(10)*time.Second, 5))
使用此方法,它会为您缓存! :)
缓存客户端对象几乎肯定是安全的,通常这样做是个好主意。 (“创建一个客户端并重用它”是一个很好的通用规则。)
我的理解是 JWT 的签名密钥通常有效期为数月甚至更长。 (Auth0's documentation notes that its JWKS documents only ever have a single key, but it will issue signed tokens all the time, so the keys must be valid for "a while".) RFC 7517 没有在 JWKS 或单个 JWK 上定义任何与过期相关的参数,我认为最好的做法是在 JWKS 端点上使用普通的 HTTP 缓存控件来偶尔刷新它,但不要那么频繁.