Authentication 使用请求与上下文的身份验证

Authentication 使用请求与上下文的身份验证,authentication,go,design-patterns,Authentication,Go,Design Patterns,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

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)
,而只需要检查
http.Request
附带的
上下文是否包含某些身份验证信息


它们似乎是等价的。那么,每种方法的技术优势/劣势是什么?如果它们确实是等价的,那么对于实际生产代码来说,哪一个更适合使用?

我认为您的示例有点混乱。似乎包含处理程序中的授权和中间件包装处理程序中的授权的组合

处理程序中的授权(使用getCurrentUser) 这是第一个示例,从请求处理程序调用一些
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)) 
    }
}
比较 从一开始就注意到这一点很重要;虽然我已经将这两个部分拆分为在处理程序中进行身份验证和在中间件中进行身份验证,但每个部分都有自己的用法。您没有理由不能交换授权发生的方式。e、 g.在中间件中使用
getCurrentUser(请求)
进行授权,是否应该在下面讨论

分解这些,你真正需要问的问题是:

“您需要在哪里访问用户结构?”

这将帮助您决定使用哪个

一般来说,将请求范围内的变量放入
context.context
中是完全有效的。跟踪信息等变量定期进入上下文。上下文的主要问题是它没有经过编译时检查,并且不是类型安全的。您在后面的代码中假设您的用户对象已在上下文中设置,这使得您的代码以一种不明显的方式耦合

context.context
方法的好处是,如果用户对象需要降低多个级别的函数调用,则不需要将其连接到整个代码库中。你可以稍后把它从上下文中拿出来。它是一个值存储桶,允许在以后的代码中具有更大的灵活性

以经办人方式授权;您可能在许多处理程序中具有相同的授权和错误处理代码。如果你试着将其提取到一个中间软件中;您很快就会发现,维护
http.Handler
接口并传递在middlware中提取的用户对象没有什么好办法。(这就是为什么在上面的示例中,用户对象被放在请求对象中的原因)

在控制器中使用
getCurrentUser(request)
进行授权的好处在于,用户结构的创建位置很明显,通过查看处理程序可以清楚地看到授权发生了什么,并且用户对象不可能不存在(假设没有返回错误)

TL;博士 取决于您需要用户对象的位置;以及有多少处理程序

  • 处理程序非常少,不需要处理程序外部的用户:
    getCurrentUser(req)
    处理程序内部
  • 许多处理程序,不需要用户外部授权:
    getCurrentUser(req)
    在middlware中,并且不把它放在上下文中(因为以后不需要它)
  • 在许多处理程序中,用户在代码的后面是必需的:在中间件中授权,将用户放在上下文中,然后将其取出

  • 您已在请求中拥有身份验证所需的所有信息。上下文也是请求属性。为请求提供auth上下文—您不提供任何新信息,只需使此信息易于接受即可。 从文件

    仅对传输的请求范围的数据使用上下文值 进程和API,不用于向函数传递可选参数