Go的许多书籍和博客中已广泛使用两种身份验证方法:
使用http.Request
:
func getCurrentUser(r *http.Request) (*User, error) {
// get JWT token or cookie and find corresponding sessions
// and account, then return the user and nil error;
// if user is not found, return a nil user and a non-nil error
}
然后处理程序函数调用getCurrentUser以获取每个用户 请求。可以使用环绕的函数装饰器 其他处理程序,并在其他处理程序功能之前检查身份验证 被执行。
func secretInfoHandler (w http.ResponseWriter, r * http.Request) {
user, err := getCurrentUser(r)
if err != nil {
// write http.Unauthorized, and return
}
// otherwise, process request return data
}
func MustAuthenticate (h http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
// chekc authentication, if pass:
h.ServeHTTP(w, r)
// if fail:
w.WriteHeader(http.StatusUnauthorized)
return
}
}
使用context.Context
:
// use the same getCurrentUser() as the one above
func MustAuthenticate (h http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
user, err := getCurrentUser(r)
if err != nil {
// write error code then return
}
ctx := context.WithValue(r.Context(), someKey, someValue)
h(w, r.WithContext(ctx))
}
}
然后针对每个处理程序(例如secretInfoHandler
),而不是调用getCurrentUser(r *http.Request)
,我只需要检查Context
随附的http.Request
是否包含某些身份验证信息
它们似乎是等效的。那么,每种方法的技术优势/劣势是什么?如果它们确实等效,那么使用哪种代码更适合实际的生产代码?
答案 0 :(得分:1)
我认为您的示例有些混乱。似乎包含处理程序中的授权和中间件包装处理程序中的授权的组合。
这是第一个示例,从请求处理程序中调用一些getCurrentUser(request)
函数。
func secretInfoHandler (w http.ResponseWriter, r * http.Request) {
user, err := getCurrentUser(r)
if err != nil {
// write http.Unauthorized, and return
}
// otherwise, process request return data
}
^^这是从您的示例中获取的,但是您还包括了MustAuthenticate
中间件,我认为这里与它无关。
这是您的第二个示例,使用context.Context
的键值存储区作为将值向下发送到处理程序的一种方法。
func MustAuthenticate (h http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
user, err := getCurrentUser(r)
if err != nil {
// write error code then return
}
ctx := context.WithValue(r.Context(), someKey, someValue)
h.ServeHTTP(w, r.WithContext(ctx))
}
}
从一开始就必须注意;虽然我将这两者分为处理程序中的auth和中间件中的auth,每个都有自己的 using 。没有理由不能交换授权发生的方式。例如下面讨论是否使用getCurrentUser(request)
在中间件中进行授权。
简而言之,您真正要问的问题是:
“您需要在哪里访问用户结构?”
这将帮助您决定使用哪个。
通常,将请求范围的变量放在context.Context
中是完全有效的。诸如跟踪信息之类的变量定期进入上下文。上下文的主要问题是它没有检查编译时间,也不安全输入。您将在以后的代码中假设已在上下文中设置了用户对象,这使您的代码以一种非显而易见的方式耦合。
context.Context
方法的好处是,如果您的用户对象需要下降多个级别的函数调用,则无需在整个代码库中进行连接。您可以稍后将其移出上下文。这是一个值存储桶,以后可以在代码中提供更大的灵活性。
具有处理程序方法中的授权;您可能在许多处理程序中具有相同的授权和错误处理代码。如果尝试将其提取到中间件中;您会很快发现,没有很好的方法来维护http.Handler
接口并传递从中间件中提取的用户对象。 (这就是在上面的示例中将用户对象放在请求对象内的原因)。
使用getCurrentUser(request)
在控制器中进行授权的好处是,很明显在哪里创建了用户结构,很明显查看该处理程序时发生了什么授权,并且无法显示用户对象(假设没有错误返回)。
取决于用户对象的位置;以及有多少个处理程序。
getCurrentUser(req)
位于处理程序内部getCurrentUser(req)
,也不要将其放在上下文中(因为以后不需要)答案 1 :(得分:0)
您已经在请求中拥有了身份验证所需的所有信息。上下文也是请求属性。提供具有auth上下文的请求,您不会提供任何新信息,而只是使此信息易于接受。 从文档
仅对传递的请求范围的数据使用上下文值 流程和API,而不是用于将可选参数传递给函数。