前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >几种封装 HTTP Authorization 的分装方式

几种封装 HTTP Authorization 的分装方式

作者头像
老麦
发布2024-01-29 15:36:22
1380
发布2024-01-29 15:36:22
举报
文章被收录于专栏:Go与云原生Go与云原生

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

原文链接: 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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 熊猫云原生Go 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 几种常见的 Authorization 封装方式
    • 1. 直接封装
      • 2. Context 传递
        • 2.1 定义 Context Key 类型
        • 2.2 通过 Context 获取验证信息
      • 3. DefaultHeader 传递
        • 4. 接口传递
          • 4.1 定义接口
          • 4.2 通过接口获取验证信息
          • 4.3. 案例扩展
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档