几种封装 HTTP Authorization 的分装方式

2024-01-29 15:36:22 浏览数 (3)

建议点击 查看原文 查看最新内容。

原文链接: https://typonotes.com/posts/2024/01/25/authz-in-http-request/

大家都知道, 在做 HTTP 请求的时候, 通常需要提供 账号名和密码, 例如

代码语言:javascript复制
$ curl -u username:password http://api.example.com

其实, 这种就是 HTTP Authentication[1] 中的 Basic 模式(Schema)

翻译一下

  1. 首先将账号密码使用 冒号: 链接
  2. 随后进行 base64 编码
  3. 最后放在 Header 的 Authorization 中。
代码语言:javascript复制
$ val=base64("username:password")
$ curl -H "Authorization: Basic ${username:password} http://api.example.com

除了 Basic 之外, HTTP 标准 Schema[2] 包括以下

  • Basic: 常见
  • Bearer: 常见
  • Digest
  • HOBA
  • Mutual
  • Negotiate / NTLM
  • VAPID
  • SCRAM
  • AWS4-HMAC-SHA256

除了以上之外, 你当然可以 自定义 自己服务器的 验证 模式, 走非标路线让黑客琢磨不透。

几种常见的 Authorization 封装方式

分享几种我见过的封装方式

1. 直接封装

这种数据比较初级阶段, 只提供 固定验证方式的封装。

如果需要再增加一种验证方式, 例如 Bearer Token,就比较无力了。

代码语言:javascript复制
type BasicConfig struct {
 Username string `json:"user"`
 Password string `json:"name"`
}

func (bc *BasicConfig) request(method string, url string, body io.Reader) {
 req, _ := http.NewRequest(method, url, body)

 // 硬编码
 auth := bc.Username   ":"   bc.Password
 authz := base64.StdEncoding.EncodeToString([]byte(auth))
 req.Header.Set("Authorization", "Basic " authz)

 _, _ = http.DefaultClient.Do(req)
}

2. Context 传递

这种方式使用 Context 进行验证参数的传递, 具有一定扩展性, 依旧存在 枚举 验证方式的问题。

2.1 定义 Context Key 类型

注意:在定义 context key 的时候, 不能直接使用 简单类型, 例如 stirng, int 等。而是 通过这些简单类型创建一个新类型,再使用。 这是类型的基础知识!!!

代码语言:javascript复制
type contextKey string

func (c contextKey) String() string {
 return "auth "   string(c)
}

var (
 // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request.
 ContextOAuth2 = contextKey("token")
 // ContextBasicAuth takes BasicAuth as authentication for the request.
 ContextBasicAuth = contextKey("basic")
 // ContextAccessToken takes a string oauth2 access token as authentication for the request.
 ContextAccessToken = contextKey("accesstoken")
 // ContextAPIKey takes an APIKey as authentication for the request
 ContextAPIKey = contextKey("apikey")
)

源代码在 go-bamboo-v1/configuation.go - Github[3]

2.2 通过 Context 获取验证信息

接下来, 在构建 request 的时候, 就通过 ctx.Value 获取 相应值。如果 断言 成功, 则添加到 HTTP Header 中。

代码语言:javascript复制
// prepareRequest build the request
func (c *APIClient) prepareRequest(
 ctx context.Context) (localVarRequest *http.Request, err error) {

 if ctx != nil {
  // add context to the request
  localVarRequest = localVarRequest.WithContext(ctx)
  // Walk through any authentication.
  // OAuth2 authentication
  if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok {
   // We were able to grab an oauth2 token from the context
   var latestToken *oauth2.Token
   if latestToken, err = tok.Token(); err != nil {
    return nil, err
   }
   latestToken.SetAuthHeader(localVarRequest)
  }
  // Basic HTTP Authentication
  if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok {
   localVarRequest.SetBasicAuth(auth.UserName, auth.Password)
  }
  // AccessToken Authentication
  if auth, ok := ctx.Value(ContextAccessToken).(string); ok {
   localVarRequest.Header.Add("Authorization", "Bearer " auth)
  }
 }
 return localVarRequest, nil
}

源代码在 go-bamboo-v1/api_client.go - Github[4]

3. DefaultHeader 传递

这种方式就解决了 枚举 带来的有限性。简单粗暴

在定义配置的时候, 直接设置一个 DefaultHeader 来承载所有。当然, 这个 DefaultHeader 不仅仅只用来保存验证方式。

代码语言:javascript复制
// Configuration provides the configuration to connect
type Configuration struct {
 DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
}

源代码在 go-bamboo-v1/configuration#L52 - Github[5]

在初始化 Request 的时候, 直接从 Map 转移到 Request Header 中即可。

代码语言:javascript复制
// prepareRequest build the request
func (c *APIClient) prepareRequest(
 ctx context.Context, cfg *Configuration) (localVarRequest *http.Request, err error) {

 for header, value := range cfg.DefaultHeader {
  localVarRequest.Header.Add(header, value)
 }

 return localVarRequest, nil
}

源代码在 go-bamboo-v1/api_client.go - Github[6]

4. 接口传递

这种方式同样不存在 枚举 的局限性。同时看起来要比 DefaultHeader 更优雅。

4.1 定义接口

首先定义一个接口, 用于返回 HTTP Request Authorization 的值(包含 验证模式验证值)。

代码语言:javascript复制
type Authorizer interface {
 Authorization() string
}

在 Client 作为字段使用, 并在初始化 Client 的时候传入

代码语言:javascript复制
type Client struct {
 authorizer Authorizer // User credentials
}

func NewClient(httpClient *http.Client, creds Authorizer) *Client {
 c := &Client{
  client:     httpClient,
  BaseURL:    baseURL,
  authorizer: creds,
 }
 return c
}
4.2 通过接口获取验证信息

在构建 HTTP Request, 直接调用 Authorzer 接口即可返回值。

代码语言:javascript复制
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {

 req, err := http.NewRequest(method, u.String(), buf)
 if err != nil {
  return nil, err
 }

 creds := c.authorizer
 req.Header.Set("Authorization", creds.Authorization())
 req.Header.Set("Accept", "application/json")

 return req, nil
}

源码在 go-bamboo/client.go#L100 - Github[7]

4.3. 案例扩展

注意:这个案例可以扩展, 同时返回 Header 名称和值

代码语言:javascript复制
type Authorizer interface {
 Authorization() (string, string)
}


func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {

 req, err := http.NewRequest(method, u.String(), buf)
 if err != nil {
  return nil, err
 }

 k, creds := c.authorizer
 req.Header.Set(k, creds)
 req.Header.Set("Accept", "application/json")

 return req, nil
}

参考资料

[1]

HTTP Authentication: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication

[2]

HTTP 标准 Schema: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes

[3]

go-bamboo-v1/configuation.go - Github: https://github.com/gfleury/go-bitbucket-v1/blob/6e30c5760c87b9095bafc130f209edcee4903a19/configuration.go#L15

[4]

go-bamboo-v1/api_client.go - Github: https://github.com/gfleury/go-bitbucket-v1/blob/6e30c5760c87b9095bafc130f209edcee4903a19/api_client.go#L288

[5]

go-bamboo-v1/configuration#L52 - Github: https://github.com/gfleury/go-bitbucket-v1/blob/6e30c5760c87b9095bafc130f209edcee4903a19/configuration.go#L52

[6]

go-bamboo-v1/api_client.go - Github: https://github.com/gfleury/go-bitbucket-v1/blob/6e30c5760c87b9095bafc130f209edcee4903a19/api_client.go#L293

[7]

go-bamboo/client.go#L100 - Github: https://github.com/tangx/go-bamboo/blob/b50568b35addef27a4bd0f04b841db86b50feb70/client.go#L100

0 人点赞