如何使用Golang实现一个API网关

在我们的配置里边也应该是支持多个节点配置的。

复制代码
{
“upstreams”: [
{
“upstream”: “http://api.qingyunke.com”,
“path”: “/jiqieren/”,
“trim_path”: true,
“is_auth”: true
}
],

}
复制代码
upstreams:上游服务器

upstream:上游服务器地址

path:路径,如果以斜线结尾的话代表拦截所有以 /jiqiren/开头的链接

trim_path:剔除路径,因为上游服务器中其实并不包含 /jiqiren/ 这段的,所以要踢掉这块

is_auth:是否是授权链接

其实至此的上游的链接已经配置好了,下面我们来配置一下授权相关的配置。现在我实现的这个版本里边允许同时存在多个授权类型。满足任何一个即可进行接口的调用。我们先简单配置一个bearer的版本。

复制代码
{

“auth_items”: {
“Bearer”: {
“oauth_type”: “BearerConfig”,
“configs”: {
“file”: “bearer.json”
}
}
}
}
复制代码
Bearer 对应的Model的意思是说,要引用配置文件的类型,对应的文件是 bearer.json

对应的文件内容如下

复制代码
{
“GnPIymAqtPEodx2di0cS9o1GP9QEM2N2-Ur_5ggvANwSKRewH2DLmw”: {
“interfaces”: [
“/jiqieren/api.php”
],
“headers”: {
“TenantId”: “100860”
}
}
}
复制代码
其实就是一个Key对应了他能调用那些接口,还有他给上游服务器传递那些信息。因为Token的其实一般不光是能不能调用,同时他还代表了某一个服务,或者说某一个使用者,对应的,我们可以将这些信息,放到请求头中传递给上游服务器。就可以做到虽然上游服务器,并不知道Token但是上游服务器知道谁能够调用它。

下面我们来说一下这个项目是如何实现的。其实,整个功能简单的描述起来就是一个带了Token解析、鉴权的反向代理。但是本质上他还是一个反向代理,我们可以直接使用Golang自带的反向代理。

核心代码如下。

复制代码
package main

import (
“./Configs”
“./Server”
“encoding/json”
“flag”
“fmt”
“io/ioutil”
“log”
“net/http”
“net/http/httputil”
“net/url”
“os”
“strings”
)

func main() {
var port int
var config string

flag.IntVar(&port, "port", 80, "server port")
flag.StringVar(&config, "config", "", "mapping config")

flag.Parse()

if config == "" {
    log.Fatal("not found config")
}

if fileExist(config) == false {
    log.Fatal("not found config file")
}

data, err := ioutil.ReadFile(config)
if err != nil {
    log.Fatal(err)
}

var configInstance Configs.Config
err = json.Unmarshal(data, &configInstance)
if err != nil {
    log.Fatal(err)
}

auths := make(map[string]Server.IAuthInterface)

if configInstance.AuthItems != nil {
    for name, configItem := range configInstance.AuthItems {
        auth_item := Server.GetAuthFactoryInstance().CreateAuthInstance(configItem.OAuthType)

        if auth_item == nil {
            continue
        }

        auth_item.InitWithConfig(configItem.Configs)
        auths[strings.ToLower(name)] = auth_item
        log.Println(name, configItem)
    }
}

for i := 0; i < len(configInstance.Upstreams); i++ {
    up := configInstance.Upstreams[i]
    u, err := url.Parse(up.Upstream)

    log.Printf("{%s} => {%s}\r\n", up.Application, up.Upstream)

    if err != nil {
        log.Fatal(err)
    }

    rp := httputil.NewSingleHostReverseProxy(u)

    http.HandleFunc(up.Application, func(writer http.ResponseWriter, request *http.Request) {
        o_path := request.URL.Path

        if up.UpHost != "" {
            request.Host = up.UpHost
        } else {
            request.Host = u.Host
        }

        if up.TrimApplication {
            request.URL.Path = strings.TrimPrefix(request.URL.Path, up.Application)
        }

        if up.IsAuth {
            auth_value := request.Header.Get("Authorization")
            if auth_value == "" {
                writeUnAuthorized(writer)
                return
            }

            sp_index := strings.Index(auth_value, " ")
            auth_type := auth_value[:sp_index]
            auth_token := auth_value[sp_index+1:]

            if auth_instance, ok := auths[strings.ToLower(auth_type)]; ok {
                err, headers := auth_instance.GetAuthInfo(auth_token, o_path)
                if err != nil {
                    writeUnAuthorized(writer)
                } else {
                    if headers != nil {
                        for k, v := range headers {
                            request.Header.Add(k, v)
                        }
                    }
                    rp.ServeHTTP(writer, request)
                }
            } else {
                writeUnsupportedAuthType(writer)
            }
        } else {
            rp.ServeHTTP(writer, request)
        }
    })
}

log.Printf("http server start on :%d\r\n", port)
http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
log.Println("finsh")

}

func writeUnsupportedAuthType(writer http.ResponseWriter) () {
writer.Header().Add(“Content-Type”, “Application/json”)
writer.WriteHeader(http.StatusBadRequest)
writer.Write([]byte("{“status”:“unsupported authorization”}"))
}

func writeUnAuthorized(writer http.ResponseWriter) {
writer.Header().Add(“Content-Type”, “Application/json”)
writer.WriteHeader(http.StatusUnauthorized)
writer.Write([]byte("{“status”:“un-authorized”}"))
}

func fileExist(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}
复制代码
最核心的代码不足150行,简单点说就是,在反向代理中间加上了鉴权的逻辑。当然鉴权的逻辑,我做了一层抽象,现在是通过配置文件来进行动态修改的。

复制代码
package Server

import (
“log”
“strings”
)

type IAuthInterface interface {
GetAuthInfo(token string, url string) (err error, headers map[string]string)
InitWithConfig(config map[string]string)
}

type AuthFactory struct {
}

var auth_factory_instance AuthFactory

func init() {
auth_factory_instance = AuthFactory{}
}

func GetAuthFactoryInstance() *AuthFactory {
return &auth_factory_instance
}

func (this *AuthFactory) CreateAuthInstance(t string) IAuthInterface {
if strings.ToLower(t) == “bearer” {
return &BeareAuth{}
}

if strings.ToLower(t) == "bearerconfig" {
    return &BearerConfigAuth{}
}

log.Fatalf("%s 是不支持的类型 \r\n", t)
return nil

}
复制代码
复制代码
package Server

import (
“encoding/json”
“errors”
“io/ioutil”
“log”
)

type BearerConfigItem struct {
Headers map[string]string json:"headers"
Interfaces []string json:"interfaces"
}

type BearerConfigAuth struct {
Configs map[string]*BearerConfigItem // token =》 config item
}

func (this *BearerConfigAuth) GetAuthInfo(token string, url string) (err error, headers map[string]string) {
configItem := this.Configs[token]
if configItem == nil {
err = errors.New(“not found token”)
return
}

if IndexOf(configItem.Interfaces, url) == -1 {
    err = errors.New("un-authorized")
    return
}

headers = make(map[string]string)
for k, v := range configItem.Headers {
    headers[k] = v
}

return

}

func (this *BearerConfigAuth) InitWithConfig(config map[string]string) {
cFile := config[“file”]
if cFile == “” {
return
}

data, err := ioutil.ReadFile(cFile)
if err != nil {
    log.Panic(err)
}

var m map[string]*BearerConfigItem

//this.Configs = make(map[string]*BearerConfigItem)
err = json.Unmarshal(data, &m)
if err != nil {
    log.Panic(err)
}

this.Configs = m

}

func IndexOf(array []string, item string) int {
for i := 0; i < len(array); i++ {
if array[i] == item {
return i
}
}

return -1

}
深圳网站建设www.sz886.com

猜你喜欢

转载自blog.csdn.net/weixin_45121123/article/details/105973767
今日推荐