introduce
Through a complete example, in gogf/gf Add Basic Auth middleware to microservices.
What is HTTP Basic Auth middleware? Basic Auth middleware will intercept each API request and verify the verification of Basic Auth or X-API-Key.
We will use rk-boot To start gogf/gf Microservices.
rk-boot Is a framework that can start a variety of Web services through YAML. Please refer to the last chapter of this article for details rk-boot Details.
Please visit the following address for a complete tutorial: https://rkdocs.netlify.app/cn
install
go get github.com/rookie-ninja/rk-boot/gf
Quick start
1. Create boot yaml
boot. The yaml file describes gogf/gf Meta information required for framework startup.
To verify, we launched the following options:
- commonService: commonService contains a series of general API s. details
- interceptors.auth: Basic Auth middleware. The default user name and password are user:pass.
--- gf: - name: greeter # Required port: 8080 # Required enabled: true # Required commonService: enabled: true # Optional interceptors: auth: enabled: true # Optional basic: ["user:pass"] # Optional
2. Create main go
Add / v1/greeter API.
If you want to add Swagger validation options to your API, refer to swag security Official website, we will introduce it in other examples.
// 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. Folder structure
$ tree . ├── boot.yaml ├── go.mod ├── go.sum └── main.go 0 directories, 4 files
4. Start main go
$ 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. Verification
Without Basic Auth, we get 401 error code.
401
$ 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 is provided. For security reasons, Auth in the Request Header needs to be encoded with Base64. We Base64 encode the user:pass string.
$ 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!"}
Use X-API-Key authorization
1. Modify boot yaml
In this step, we start X-API-Key, and the value of the key is token.
--- gf: - name: greeter # Required port: 8080 # Required enabled: true # Required commonService: enabled: true # Optional interceptors: auth: enabled: true # Optional apiKey: ["token"] # Optional
2. Start main go
$ 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. Verification
In the same case, we get 401 error code without providing X-API-Key.
401
$ 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
$ 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!"}
Ignore request path
We can add a series of API request paths to make the middleware ignore and verify these API requests.
--- 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
How to add basic auth & x-api-key input box in Swagger UI?
We use swag Generate the config file required by Swagger UI, so add some comments to the code.
- Add the following annotation in the main() function to define security.
// @securityDefinitions.basic BasicAuth // @securityDefinitions.apikey ApiKeyAuth // @in header // @name X-API-Key
- Add the following annotation to the Handler function.
// @Security ApiKeyAuth // @Security BasicAuth
Complete example
- main.go
// 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 }
- Run the swag command
$ swag init $ tree . ├── boot.yaml ├── docs │ ├── docs.go │ ├── swagger.json │ └── swagger.yaml ├── go.mod ├── go.sum └── main.go 1 directory, 7 files
- boot.yaml Add SW enabled & sw. Jsonpath (path to the swagger.json file)
--- 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
Run main Go and visit http://localhost:8080/sw
Introduction to rk boot
rk-boot Is a framework that can start a variety of Web services through YAML.
It is similar to Spring boot. By integrating rk XXX series libraries, you can start a variety of Web frameworks. Of course, users can also customize the RK XXX library and integrate it into rk boot.
Rk boot highlights
Launch different Web frameworks through YAML files in the same format.
For example, we can start grpc, gin, echo and goframe frameworks in one process through the following files. Unify the micro service layout within the team.
- Dependent installation
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
--- 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
// 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()) }
- verification
# 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}
Web framework supported by rk boot
Welcome to contribute a new Web framework to the RK boot series.
reference resources docs & rk-gin As an example.
frame | Development status | install | rely on |
---|---|---|---|
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 introduction
rk-gf Used to launch via YAML gogf/gf Web services.
Supported features
According to the YAML file initialization example below, if it is external, the native usage will be maintained.
example | introduce |
---|---|
ghttp.Server | Primordial gogf/gf |
Config | Primordial spf13/viper Parameter instance |
Logger | Primordial uber-go/zap Log instance |
EventLogger | Used to record RPC request logs, using rk-query |
Credential | Used to pull Credential from a remote service, such as ETCD |
Cert | Obtain the TLS/SSL certificate from the remote service (ETCD, etc.) and start SSL/TLS |
Prometheus | Start the Prometheus client and push it to as needed pushgateway |
Swagger | Launch Swagger UI locally |
CommonService | Expose common API s |
TV | TV web page, showing the basic information of micro services |
StaticFileHandler | Start the static file download service in the form of Web, and the background storage supports the local file system and pkger |
Supported Middleware
rk-gf will initialize the middleware according to the YAML file.
Middleware | Description |
---|---|
Metrics | Collect RPC Metrics and start prometheus |
Log | use rk-query Record each RPC log |
Trace | Collect RPC call chain and send data to stdout, local file or jaeger open-telemetry/opentelemetry-go. |
Panic | Recover from panic for RPC requests and log it. |
Meta | Collect the service meta information and add it to the return Header |
Auth | Middleware supporting basic auth & API key authentication |
RateLimit | RPC speed limiting Middleware |
Timeout | RPC timeout Middleware |
CORS | CORS Middleware |
JWT | JWT validation |
Secure | Server side security middleware |
CSRF | CSRF Middleware |
GoFrame complete YAML configuration
--- #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: []