前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GoFrame 框架:Basic Auth 中间件

GoFrame 框架:Basic Auth 中间件

原创
作者头像
尹东勋
修改2022-01-04 18:51:17
9610
修改2022-01-04 18:51:17
举报

介绍

通过一个完整例子,在 gogf/gf 微服务中添加 Basic Auth 中间件。

什么是 HTTP Basic Auth 中间件? Basic Auth 中间件会对每一个 API 请求进行拦截,并验证 Basic Auth 或者 X-API-Key 的验证。

我们将会使用 rk-boot 来启动 gogf/gf 微服务。

rk-boot 是一个可通过 YAML 启动多种 Web 服务的框架。请参考本文最后章节,了解 rk-boot 细节。

请访问如下地址获取完整教程:https://rkdocs.netlify.app/cn

安装

代码语言:txt
复制
go get github.com/rookie-ninja/rk-boot/gf

快速开始

1.创建 boot.yaml

boot.yaml 文件描述了 gogf/gf 框架启动时所需要的元信息。

为了验证,我们启动了如下几个选项:

  • commonService:commonService 里包含了一系列通用 API。详情
  • interceptors.auth: Basic Auth 中间件,默认用户名密码为 user:pass。
代码语言:txt
复制
---
gf:
  - name: greeter                   # Required
    port: 8080                      # Required
    enabled: true                   # Required
    commonService:
      enabled: true                 # Optional
    interceptors:
      auth:
        enabled: true               # Optional
        basic: ["user:pass"]        # Optional

2.创建 main.go

添加 /v1/greeter API。

如果想要在自己的 API 中添加 Swagger 验证选项,请参考 swag security 官网,我们会在其他的例子中介绍。

代码语言:txt
复制
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/rookie-ninja/rk-boot"
	"github.com/rookie-ninja/rk-boot/gf"
	"net/http"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Register handler
	gfEntry := rkbootgf.GetGfEntry("greeter")
	gfEntry.Server.BindHandler("/v1/greeter", func(ctx *ghttp.Request) {
		ctx.Response.WriteHeader(http.StatusOK)
		ctx.Response.WriteJson(&GreeterResponse{
			Message: fmt.Sprintf("Hello %s!", ctx.GetQuery("name")),
		})
	})

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

type GreeterResponse struct {
	Message string
}

3.文件夹结构

代码语言:txt
复制
$ tree
.
├── boot.yaml
├── go.mod
├── go.sum
└── main.go

0 directories, 4 files

4.启动 main.go

代码语言:txt
复制
$ go run main.go

2022-01-04T17:45:56.925+0800    INFO    boot/gf_entry.go:1050   Bootstrap gfEntry       {"eventId": "984ea465-22cf-4cf3-a734-893fd3dc79e1", "entryName": "greeter"}
------------------------------------------------------------------------
endTime=2022-01-04T17:45:56.925701+08:00
startTime=2022-01-04T17:45:56.924974+08:00
elapsedNano=726802
timezone=CST
ids={"eventId":"984ea465-22cf-4cf3-a734-893fd3dc79e1"}
app={"appName":"rk","appVersion":"","entryName":"greeter","entryType":"GfEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"commonServiceEnabled":true,"commonServicePathPrefix":"/rk/v1/","gfPort":8080}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost
operation=Bootstrap
resCode=OK
eventStatus=Ended
EOE

5.验证

在不提供 Basic Auth 的情况下,我们得到了 401 错误码。

401

代码语言:txt
复制
$ curl -X GET localhost:8080/rk/v1/healthy
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"Missing authorization, provide one of bellow auth header:[Basic Auth]",
        "details":[]
    }
}

$ curl -X GET "localhost:8080/v1/greeter?name=rk-dev
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"Missing authorization, provide one of bellow auth header:[Basic Auth]",
        "details":[]
    }
}

200

提供 Basic Auth,出于安全考虑,Request Header 里的 Auth 需要用 Base64 进行编码。我们对 user:pass 字符串进行了 Base64 编码。

代码语言:txt
复制
$ curl localhost:8080/rk/v1/healthy -H "Authorization: Basic dXNlcjpwYXNz"
{"healthy":true}

$ curl "localhost:8080/v1/greeter?name=rk-dev" -H "Authorization: Basic dXNlcjpwYXNz"
{"Message":"Hello rk-dev!"}

使用 X-API-Key 授权

1.修改 boot.yaml

这一步,我们启动 X-API-Key,key 的值为 token。

代码语言:txt
复制
---
gf:
  - name: greeter                   # Required
    port: 8080                      # Required
    enabled: true                   # Required
    commonService:
      enabled: true                 # Optional
    interceptors:
      auth:
        enabled: true               # Optional
        apiKey: ["token"]           # Optional

2.启动 main.go

代码语言:txt
复制
$ go run main.go

2022-01-04T17:53:44.120+0800    INFO    boot/gf_entry.go:1050   Bootstrap gfEntry       {"eventId": "161515d7-d2fb-4457-a24d-590edeb71bdf", "entryName": "greeter"}
------------------------------------------------------------------------
endTime=2022-01-04T17:53:44.12083+08:00
startTime=2022-01-04T17:53:44.120248+08:00
elapsedNano=582008
timezone=CST
ids={"eventId":"161515d7-d2fb-4457-a24d-590edeb71bdf"}
app={"appName":"rk","appVersion":"","entryName":"greeter","entryType":"GfEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"commonServiceEnabled":true,"commonServicePathPrefix":"/rk/v1/","gfPort":8080}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost
operation=Bootstrap
resCode=OK
eventStatus=Ended
EOE

3.验证

同样的情况,在不提供 X-API-Key 的情况下,我们得到了 401 错误码。

401

代码语言:txt
复制
$ curl localhost:8080/rk/v1/healthy
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"Missing authorization, provide one of bellow auth header:[X-API-Key]",
        "details":[]
    }
}

$ curl "localhost:8080/v1/greeter?name=rk-dev"
{
    "error":{
        "code":401,
        "status":"Unauthorized",
        "message":"Missing authorization, provide one of bellow auth header:[X-API-Key]",
        "details":[]
    }
}

200

代码语言:txt
复制
$ curl localhost:8080/rk/v1/healthy -H "X-API-Key: token"
{"healthy":true}

$ curl "localhost:8080/v1/greeter?name=rk-dev" -H "X-API-Key: token"
{"Message":"Hello rk-dev!"}

忽略请求路径

我们可以添加一系列 API 请求路径,让中间件忽略验证这些 API 请求。

代码语言:txt
复制
---
gf:
  - name: greeter                                          # Required
    port: 8080                                             # Required
    enabled: true                                          # Required
    commonService:
      enabled: true                                        # Optional
    interceptors:
      auth:
        enabled: true                                      # Optional
        basic: ["user:pass"]                               # Optional
        ignorePrefix: ["/rk/v1/healthy", "/v1/greeter"]    # Optional

Swagger UI

如何在 Swagger UI 里添加 Basic Auth & X-API-Key 输入框?

我们使用 swag 生成 Swagger UI 所需要的 config 文件,所以,在代码里添加一些注释

  • 在 main() 函数添加如下 annotation,定义 security。
代码语言:txt
复制
// @securityDefinitions.basic BasicAuth
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
  • 在 Handler 函数添加如下 annotation。
代码语言:txt
复制
// @Security ApiKeyAuth
// @Security BasicAuth

完整例子

  • main.go
代码语言:txt
复制
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"fmt"
	"github.com/labstack/echo/v4"
	"github.com/rookie-ninja/rk-boot"
	"net/http"
)

// @title RK Swagger for Echo
// @version 1.0
// @description This is a greeter service with rk-boot.
// @securityDefinitions.basic BasicAuth

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Register handler
	boot.GetEchoEntry("greeter").Echo.GET("/v1/greeter", Greeter)

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}

// @Summary Greeter service
// @Id 1
// @version 1.0
// @produce application/json
// @Param name query string true "Input name"
// @Security ApiKeyAuth
// @Security BasicAuth
// @Success 200 {object} GreeterResponse
// @Router /v1/greeter [get]
func Greeter(ctx echo.Context) error {
	return ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.QueryParam("name")),
	})
}

type GreeterResponse struct {
	Message string
}
  • 运行 swag 命令
代码语言:txt
复制
$ swag init

$ tree
.
├── boot.yaml
├── docs
│   ├── docs.go
│   ├── swagger.json
│   └── swagger.yaml
├── go.mod
├── go.sum
└── main.go

1 directory, 7 files
  • boot.yaml 添加 sw.enabled & sw.jsonPath (swagger.json 文件的路径)
代码语言:txt
复制
---
gf:
  - name: greeter                   # Required
    port: 8080                      # Required
    enabled: true                   # Required
    sw:
      enabled: true                 # Optional
      jsonPath: "docs"              # Optional
    interceptors:
      auth:
        enabled: true               # Optional
        basic: ["user:pass"]        # Optional

运行 main.go 并且访问 http://localhost:8080/sw

rk-boot 介绍

rk-boot 是一个可通过 YAML 启动多种 Web 服务的框架。

有点类似于 Spring boot。通过集成 rk-xxx 系列库,可以启动多种 Web 框架。当然,用户也可以自定义 rk-xxx 库集成到 rk-boot 中。

rk-boot 亮点

通过同样格式的 YAML 文件,启动不同 Web 框架。

比如,我们可以通过如下文件,在一个进程中同时启动 gRPC, Gin, Echo, GoFrame 框架。统一团队内部的微服务布局。

  • 依赖安装
代码语言:txt
复制
go get github.com/rookie-ninja/rk-boot/grpc
go get github.com/rookie-ninja/rk-boot/gin
go get github.com/rookie-ninja/rk-boot/echo
go get github.com/rookie-ninja/rk-boot/gf
  • boot.yaml
代码语言:txt
复制
---
grpc:
  - name: grpc-server
    port: 8080
    enabled: true
    commonService:
      enabled: true
gin:
  - name: gin-server
    port: 8081
    enabled: true
    commonService:
      enabled: true
echo:
  - name: echo-server
    port: 8082
    enabled: true
    commonService:
      enabled: true
gf:
  - name: gf-server
    port: 8083
    enabled: true
    commonService:
      enabled: true
  • main.go
代码语言:txt
复制
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main

import (
	"context"
	"github.com/rookie-ninja/rk-boot"
	_ "github.com/rookie-ninja/rk-boot/echo"
	_ "github.com/rookie-ninja/rk-boot/gf"
	_ "github.com/rookie-ninja/rk-boot/gin"
	_ "github.com/rookie-ninja/rk-boot/grpc"
)

// Application entrance.
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot()

	// Bootstrap
	boot.Bootstrap(context.Background())

	// Wait for shutdown sig
	boot.WaitForShutdownSig(context.Background())
}
  • 验证
代码语言:txt
复制
# gRPC throuth grpc-gateway
$ curl localhost:8080/rk/v1/healthy
{"healthy":true}

# Gin
$ curl localhost:8081/rk/v1/healthy
{"healthy":true}

# Echo
$ curl localhost:8082/rk/v1/healthy
{"healthy":true}

# GoFrame
$ curl localhost:8083/rk/v1/healthy
{"healthy":true}

rk-boot 支持的 Web 框架

欢迎贡献新的 Web 框架到 rk-boot 系列中。

参考 docs & rk-gin 作为例子。

框架

开发状态

安装

依赖

Stable

go get github.com/rookie-ninja/rk-boot/gin

Stable

go get github.com/rookie-ninja/rk-boot/grpc

Stable

go get github.com/rookie-ninja/rk-boot/echo

Stable

go get github.com/rookie-ninja/rk-boot/gf

Testing

go get github.com/rookie-ninja/rk-boot/fiber

Testing

go get github.com/rookie-ninja/rk-boot/zero

Testing

go get github.com/rookie-ninja/rk-boot/mux

rk-gf 介绍

rk-gf 用于通过 YAML 启动 gogf/gf Web 服务。

支持的功能

根据 YAML 文件初始化如下的实例,如果是外部以来,均保持原生用法。

实例

介绍

ghttp.Server

原生 gogf/gf

Config

原生 spf13/viper 参数实例

Logger

原生 uber-go/zap 日志实例

EventLogger

用于记录 RPC 请求日志,使用 rk-query

Credential

用于从远程服务,例如 ETCD 拉取 Credential

Cert

从远程服务(ETCD 等等)中获取 TLS/SSL 证书,并启动 SSL/TLS

Prometheus

启动 Prometheus 客户端,并根据需要推送到 pushgateway

Swagger

本地启动 Swagger UI

CommonService

暴露通用 API

TV

TV 网页,展示微服务的基本信息

StaticFileHandler

启动 Web 形式的静态文件下载服务,后台存储支持本地文件系统 和 pkger.

支持的中间件

rk-gf 会根据 YAML 文件初始化中间件。

Middleware

Description

Metrics

收集 RPC Metrics,并启动 prometheus

Log

使用 rk-query 记录每一个 RPC 日志

Trace

收集 RPC 调用链,并且发送数据到 stdout, 本地文件或者 jaeger open-telemetry/opentelemetry-go.

Panic

Recover from panic for RPC requests and log it.

Meta

收集服务元信息,添加到返回 Header 中

Auth

支持 Basic Auth & API Key 验证中间件

RateLimit

RPC 限速中间件

Timeout

RPC 超时中间件

CORS

CORS 中间件

JWT

JWT 验证

Secure

服务端安全中间件

CSRF

CSRF 中间件

GoFrame 完整 YAML 配置

代码语言:txt
复制
---
#app:
#  description: "this is description"                      # Optional, default: ""
#  keywords: ["rk", "golang"]                              # Optional, default: []
#  homeUrl: "http://example.com"                           # Optional, default: ""
#  iconUrl: "http://example.com"                           # Optional, default: ""
#  docsUrl: ["http://example.com"]                         # Optional, default: []
#  maintainers: ["rk-dev"]                                 # Optional, default: []
#zapLogger:
#  - name: zap-logger                                      # Required
#    description: "Description of entry"                   # Optional
#eventLogger:
#  - name: event-logger                                    # Required
#    description: "Description of entry"                   # Optional
#cred:
#  - name: "local-cred"                                    # Required
#    provider: "localFs"                                   # Required, etcd, consul, localFs, remoteFs are supported options
#    description: "Description of entry"                   # Optional
#    locale: "*::*::*::*"                                  # Optional, default: *::*::*::*
#    paths:                                                # Optional
#      - "example/boot/full/cred.yaml"
#cert:
#  - name: "local-cert"                                    # Required
#    provider: "localFs"                                   # Required, etcd, consul, localFs, remoteFs are supported options
#    description: "Description of entry"                   # Optional
#    locale: "*::*::*::*"                                  # Optional, default: *::*::*::*
#    serverCertPath: "example/boot/full/server.pem"        # Optional, default: "", path of certificate on local FS
#    serverKeyPath: "example/boot/full/server-key.pem"     # Optional, default: "", path of certificate on local FS
#    clientCertPath: "example/client.pem"                  # Optional, default: "", path of certificate on local FS
#    clientKeyPath: "example/client.pem"                   # Optional, default: "", path of certificate on local FS
#config:
#  - name: rk-main                                         # Required
#    path: "example/boot/full/config.yaml"                 # Required
#    locale: "*::*::*::*"                                  # Required, default: *::*::*::*
#    description: "Description of entry"                   # Optional
gf:
  - name: greeter                                          # Required
    port: 8080                                             # Required
    enabled: true                                          # Required
#    description: "greeter server"                         # Optional, default: ""
#    cert:
#      ref: "local-cert"                                   # Optional, default: "", reference of cert entry declared above
#    sw:
#      enabled: true                                       # Optional, default: false
#      path: "sw"                                          # Optional, default: "sw"
#      jsonPath: ""                                        # Optional
#      headers: ["sw:rk"]                                  # Optional, default: []
#    commonService:
#      enabled: true                                       # Optional, default: false
#    static:
#      enabled: true                                       # Optional, default: false
#      path: "/rk/v1/static"                               # Optional, default: /rk/v1/static
#      sourceType: local                                   # Required, options: pkger, local
#      sourcePath: "."                                     # Required, full path of source directory
#    tv:
#      enabled:  true                                      # Optional, default: false
#    prom:
#      enabled: true                                       # Optional, default: false
#      path: ""                                            # Optional, default: "metrics"
#      pusher:
#        enabled: false                                    # Optional, default: false
#        jobName: "greeter-pusher"                         # Required
#        remoteAddress: "localhost:9091"                   # Required
#        basicAuth: "user:pass"                            # Optional, default: ""
#        intervalMs: 10000                                 # Optional, default: 1000
#        cert:                                             # Optional
#          ref: "local-test"                               # Optional, default: "", reference of cert entry declared above
#    logger:
#      zapLogger:
#        ref: zap-logger                                   # Optional, default: logger of STDOUT, reference of logger entry declared above
#      eventLogger:
#        ref: event-logger                                 # Optional, default: logger of STDOUT, reference of logger entry declared above
#    interceptors:
#      loggingZap:
#        enabled: true                                     # Optional, default: false
#        zapLoggerEncoding: "json"                         # Optional, default: "console"
#        zapLoggerOutputPaths: ["logs/app.log"]            # Optional, default: ["stdout"]
#        eventLoggerEncoding: "json"                       # Optional, default: "console"
#        eventLoggerOutputPaths: ["logs/event.log"]        # Optional, default: ["stdout"]
#      metricsProm:
#        enabled: true                                     # Optional, default: false
#      auth:
#        enabled: true                                     # Optional, default: false
#        basic:
#          - "user:pass"                                   # Optional, default: []
#        ignorePrefix:
#          - "/rk/v1"                                      # Optional, default: []
#        apiKey:
#          - "keys"                                        # Optional, default: []
#      meta:
#        enabled: true                                     # Optional, default: false
#        prefix: "rk"                                      # Optional, default: "rk"
#      tracingTelemetry:
#        enabled: true                                     # Optional, default: false
#        exporter:                                         # Optional, default will create a stdout exporter
#          file:
#            enabled: true                                 # Optional, default: false
#            outputPath: "logs/trace.log"                  # Optional, default: stdout
#          jaeger:
#            agent:
#              enabled: false                              # Optional, default: false
#              host: ""                                    # Optional, default: localhost
#              port: 0                                     # Optional, default: 6831
#            collector:
#              enabled: true                               # Optional, default: false
#              endpoint: ""                                # Optional, default: http://localhost:14268/api/traces
#              username: ""                                # Optional, default: ""
#              password: ""                                # Optional, default: ""
#      rateLimit:
#        enabled: false                                    # Optional, default: false
#        algorithm: "leakyBucket"                          # Optional, default: "tokenBucket"
#        reqPerSec: 100                                    # Optional, default: 1000000
#        paths:
#          - path: "/rk/v1/healthy"                        # Optional, default: ""
#            reqPerSec: 0                                  # Optional, default: 1000000
#      jwt:
#        enabled: true                                     # Optional, default: false
#        signingKey: "my-secret"                           # Required
#        ignorePrefix:                                     # Optional, default: []
#          - "/rk/v1/tv"
#          - "/sw"
#          - "/rk/v1/assets"
#        signingKeys:                                      # Optional
#          - "key:value"
#        signingAlgo: ""                                   # Optional, default: "HS256"
#        tokenLookup: "header:<name>"                      # Optional, default: "header:Authorization"
#        authScheme: "Bearer"                              # Optional, default: "Bearer"
#      secure:
#        enabled: true                                     # Optional, default: false
#        xssProtection: ""                                 # Optional, default: "1; mode=block"
#        contentTypeNosniff: ""                            # Optional, default: nosniff
#        xFrameOptions: ""                                 # Optional, default: SAMEORIGIN
#        hstsMaxAge: 0                                     # Optional, default: 0
#        hstsExcludeSubdomains: false                      # Optional, default: false
#        hstsPreloadEnabled: false                         # Optional, default: false
#        contentSecurityPolicy: ""                         # Optional, default: ""
#        cspReportOnly: false                              # Optional, default: false
#        referrerPolicy: ""                                # Optional, default: ""
#        ignorePrefix: []                                  # Optional, default: []
#      csrf:
#        enabled: true
#        tokenLength: 32                                   # Optional, default: 32
#        tokenLookup: "header:X-CSRF-Token"                # Optional, default: "header:X-CSRF-Token"
#        cookieName: "_csrf"                               # Optional, default: _csrf
#        cookieDomain: ""                                  # Optional, default: ""
#        cookiePath: ""                                    # Optional, default: ""
#        cookieMaxAge: 86400                               # Optional, default: 86400
#        cookieHttpOnly: false                             # Optional, default: false
#        cookieSameSite: "default"                         # Optional, default: "default", options: lax, strict, none, default
#        ignorePrefix: []                                  # Optional, default: []

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 安装
  • 快速开始
    • 1.创建 boot.yaml
      • 2.创建 main.go
        • 3.文件夹结构
          • 4.启动 main.go
            • 5.验证
              • 401
              • 200
          • 使用 X-API-Key 授权
            • 1.修改 boot.yaml
              • 2.启动 main.go
                • 3.验证
                  • 401
                  • 200
              • 忽略请求路径
              • Swagger UI
                • 完整例子
                • rk-boot 介绍
                  • rk-boot 亮点
                    • rk-boot 支持的 Web 框架
                    • rk-gf 介绍
                      • 支持的功能
                        • 支持的中间件
                          • GoFrame 完整 YAML 配置
                          相关产品与服务
                          消息队列 TDMQ
                          消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档