Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: auth & proxy #5

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions ihub/ihub-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ midwares:
- midware: "trace"
- midware: "inout"
- midware: "auth"
ibaseUrl:
ipPort: "100.2.44.60:32000"
path: "/v1/token"
approveMap:
moduleTransMap:
account-server: '{"zh-CN": "用户管理","en-US": "account-server"}'
Expand Down
18 changes: 18 additions & 0 deletions ihub/pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,21 @@ type Reply struct {
Message string `json:"message"`
Data string `json:"data"`
}

//type TokenResponse struct {
// ErrCode string `json:"errCode"`
// ErrMessage string `json:"errMessage"`
// ExceptionMsg string `json:"exceptionMsg"`
// Flag bool `json:"flag"`
// ResData struct {
// Account string `json:"account"`
// GroupId string `json:"groupId"`
// GroupName string `json:"groupName"`
// InnerCall bool `json:"innerCall"`
// Ip string `json:"ip"`
// Priority int `json:"priority"`
// RoleType int `json:"roleType"`
// UserId string `json:"userId"`
// UserType int `json:"userType"`
// } `json:"resData"`
//}
17 changes: 9 additions & 8 deletions ihub/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ type MidwareConfig struct {

// Configuration ...
type Configuration struct {
DB DBConfig `yaml:"DB"`
LOG LogConfig `yaml:"log"`
SERVER ServerConfig `yaml:"server"`
CACHE CacheConfig `yaml:"cache"`
Midwares []MidwareConfig `yaml:"midwares"`
Runmode string `yaml:"runmode"`
ApproveMap ApproveConfig `yaml:"approveMap"`
DB DBConfig `yaml:"DB"`
LOG LogConfig `yaml:"log"`
SERVER ServerConfig `yaml:"server"`
CACHE CacheConfig `yaml:"cache"`
Midwares []MidwareConfig `yaml:"midwares"`
Runmode string `yaml:"runmode"`
IbaseUrl map[string]string `yaml:"ibaseUrl"`
ApproveMap ApproveConfig `yaml:"approveMap"`
}

type ApproveConfig struct {
Expand All @@ -57,7 +58,7 @@ type ApproveConfig struct {
ModuleOperateMapAdmin map[string][]string `yaml:"moduleOperateMapAdmin"`
ModuleOperateMapGroup map[string][]string `yaml:"moduleOperateMapGroup"`
AppstoreTransMap map[string]string `yaml:"appstoreTransMap"`
OuterServicePortMap map[string]string `yaml:"outerServicePortMap"`
OuterServicePortMap map[string]int `yaml:"outerServicePortMap"`
}

var gConfig Configuration
Expand Down
6 changes: 6 additions & 0 deletions ihub/pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package constants

// Context Parameters
const Destination = "Destination"
const ClusterId = "ClusterId"
const ClusterName = "ClusterName"
const ClusterDomain = "ClusterDomain"
const NeedApprove = "NeedApprove"
const ApproveRole = "ApproveRole"

// const Role = "Role"

Expand All @@ -26,10 +28,14 @@ const ClusterStatusReseting = 2
const ClusterStatusResetSucceed = 1
const ClusterStatusResetFailed = 0

// ApproveStatus
const ApproveStatusApproving = "APPROVING"

// HTTP Header variables
const (
HTTPHeaderClusterName = "X-Cluster-Name"
HTTPHeaderTraceID = "X-Trace-ID"
HTTPHeaderAuthInfo = "X-Auth-Info"
)

// Default value for rgm
Expand Down
27 changes: 26 additions & 1 deletion ihub/pkg/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,36 @@ func GetOperatorid(moduleId int, operateName string) ([]Operatorid, error) {
return operatorid, nil
}

// ApproveInf .
type ApproveInf struct {
ID int `db:"id, omitempty"`
ResourceInfo string `db:"resource_info"`
Createtime time.Time `db:"createtime"`
Userid int `db:"userid"`
Type string `db:"type"`
Status string `db:"status"`
Cluasterid int `db:"clusterid"`
Advice string `db:"advice"`
Groupid int `db:"groupid"`
UserRole int `db:"user_role"`
ModuleName string `db:"module_name"`
URL string `db:"url"`
Method string `db:"method"`
OperateName string `db:"operate_name"`
ResourceDetail string `db:"resource_detail"`
Headers string `db:"headers"`
ApproveRole int `db:"approve_role"`
ApproveResult string `db:"approve_result"`
ApproveTime time.Time `db:"approve_time"`
IsDelete int `db:"is_delete"`
IsApproveDelete int `db:"is_approve_delete"`
}

// 向 approve_inf 插入一条记录
// resource_info resource_detail headers createtime userid module_name operate_name status clusterid
// user_role url method approve_role group_id type
// 其中 createtime 为当前时间, 使用SQL函数NOW()获取
func InsertApproveInf(resourceInfo string, resourceDetail string, headers string, userId int, moduleName string, operateName string, status string, clusterId int, userRole string, url string, method string, approveRole string, groupId int, approveType string) error {
func InsertApproveInf(resourceInfo []byte, resourceDetail []byte, headers []byte, userId int, moduleName string, operateName string, status string, clusterId int, userRole int, url string, method string, approveRole int, groupId int, approveType string) error {
// 根据 approve_inf 表查询
req := DBInstance.SQL().
InsertInto("approve_inf").
Expand Down
221 changes: 163 additions & 58 deletions ihub/pkg/handler/handler.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package handler

import (
"encoding/json"
"ihub/pkg/api"
mydb "ihub/pkg/db"
"ihub/pkg/config"
"ihub/pkg/constants"
"ihub/pkg/db"
"ihub/pkg/utils"
"net/http"
"net/http/httputil"
Expand All @@ -22,73 +25,175 @@ func Health(c *gin.Context) {
}

func Proxy(c *gin.Context) {
// 从请求头中获取X-Cluster-Name,该请求头中包含了当前请求需要访问的集群名称。
// 如果请求头中不包含X-Cluster-Name,则返回一个错误信息。
v, ok := c.Request.Header["X-Cluster-Name"]
if !ok {
rp := api.Reply{
Code: 1,
Message: "缺少集群名称",
Data: "",
}
c.JSON(http.StatusOK, rp)
return
// 判断是集群内还是集群外服务
destination, _ := c.Get(constants.Destination)
// 判断是否需要审批
needApprove, _ := c.Get(constants.NeedApprove)
runmode := config.GetConfig().Runmode

clusterDomain, exists := c.Get(constants.ClusterDomain)
if !exists {
clusterDomain = ""
}
module := c.Param("module")
endpoint := c.Param("endpoint")
// 如果是应用商店接口,需要进行接口转换,如去掉v1/helm、v1/store等
if _, ok := config.GetConfig().ApproveMap.AppstoreTransMap[endpoint]; ok {
endpoint = config.GetConfig().ApproveMap.AppstoreTransMap[endpoint]
}

// 根据集群名称获取对应的域名
// domain, err := mydb.GetDomainByClusterName(v[0])
// domain, _, err := mydb.GetDomainIdByClusterName(v[0])
nameDomainIdList, err := mydb.GetDomainIdByClusterName(v[0])
domain := nameDomainIdList[0].Domain
if err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Data: "",
// 重构url
targetURL, realPath := utils.MakeURL(
destination.(string),
clusterDomain.(string),
module,
endpoint,
runmode,
)

if needApprove.(bool) && destination.(string) == runmode {
// 添加审批
// vali调用
valiUrl := utils.MakeValiURL(targetURL, module, endpoint)
req := c.Request
method := req.Method
remote, err := url.Parse(valiUrl)
if err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}
// req.Header = c.Request.Header
req.Host = remote.Host
req.URL.Scheme = remote.Scheme
req.URL.Host = remote.Host
req.URL.Path = remote.Path
res, err := (&http.Client{}).Do(req)
if res == nil || err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}
defer res.Body.Close() // ???

// 读取响应体
resourceDetail := map[string]interface{}{}
err = json.NewDecoder(res.Body).Decode(&resourceDetail)
//body, err := httputil.DumpResponse(res, true)
if err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}
c.JSON(http.StatusOK, rp)
return
}

// 获取目标URL和真实路径
// c.Param("proxyPath")获取请求路径中的proxyPath参数,如localhost:8080/api/v1/<proxyPath>
// proxyPath = /完整路径(fullPath) = /模块名称(moudle)/真实路径(realPath)
// 目标URL(targetURL),格式为http://模块名称.default.域名
// 从响应体中读取信息
code := resourceDetail["code"].(int)
data := resourceDetail["data"].(map[string]interface{})
resourceInfo := data["resource_info"].(map[string]interface{})
if code != 0 {
rp := api.Reply{
Code: 1,
Message: "审批失败",
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}

targetURL, realPath := utils.MakeURL(domain, c.Param("proxyPath"))
var okList bool
userid, ok := data["userid"]
okList = ok
userRole, ok := data["user_role"]
okList = okList && ok
groupid, ok := data["groupid"]
okList = okList && ok
approvetype, ok := data["type"]
okList = okList && ok
username, ok := data["username"]
okList = okList && ok
groupname, ok := data["groupname"]
okList = okList && ok
nsid, ok := data["nsid"]
okList = okList && ok
if !okList {
rp := api.Reply{
Code: 1,
Message: "审批返回值缺少字段",
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}

xToken := map[string]map[string]interface{}{
"user_info": {
"user_id": userid.(int),
"nsid": nsid.(string),
"groupid": groupid.(string),
"username": username.(string),
"groupname": groupname.(string),
"user_role": userRole.(string),
},
}
// 转为json
xTokenJson, _ := json.Marshal(xToken)
resourceInfoJson, _ := json.Marshal(resourceInfo)
resourceDetailJson, _ := json.Marshal(resourceDetail)
clusterId, _ := c.Get(constants.ClusterId)
approveRole, _ := c.Get(constants.ApproveRole)
// json_to_str?
db.InsertApproveInf(resourceInfoJson, resourceDetailJson, xTokenJson, userid.(int), module, endpoint, constants.ApproveStatusApproving, clusterId.(int), userRole.(int), targetURL, method, approveRole.(int), groupid.(int), approvetype.(string))

// Parse方法将字符串解析为URL结构体,并返回一个指向URL结构体的指针和一个错误值。
remote, err := url.Parse(targetURL)
if err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Code: 0,
Message: "审批中",
Data: "",
}
c.JSON(http.StatusOK, rp)
return
} else {
// Parse方法将字符串解析为URL结构体,并返回一个指向URL结构体的指针和一个错误值。
remote, err := url.Parse(targetURL)
if err != nil {
rp := api.Reply{
Code: 1,
Message: err.Error(),
Data: "",
}
c.JSON(http.StatusOK, rp)
return
}
proxy := httputil.NewSingleHostReverseProxy(remote)
// Director属性是一个函数,该函数用于修改请求的属性,例如修改请求头、请求路径等。
// 该函数的第一个参数是一个指向http.Request类型的指针,用于获取请求的属性。
// 函数体中将请求头、请求路径等属性修改为目标URL的属性。
proxy.Director = func(req *http.Request) {
// Header属性是一个map,用于存储请求头(Headers)
req.Header = c.Request.Header
// Host属性是请求头中的Host字段,用于指定请求的主机名(模块名称.default.域名),会解析为IP地址。
req.Host = remote.Host
// Scheme属性是请求头中的Scheme字段,用于指定请求的协议(http、https)
req.URL.Scheme = remote.Scheme
// URL.Host属性是请求头中的Host字段,用于指定请求的主机名(模块名称.default.域名)
req.URL.Host = remote.Host
// URL.Path属性是请求头中的Path字段,用于指定请求的路径。
req.URL.Path = realPath
}
// ServeHTTP方法用于将请求转发到目标URL
// 第一个参数是一个ResponseWriter类型的对象,用于将响应返回给客户端。
// 第二个参数是一个指向http.Request类型的指针,用于获取请求的属性,传递给Director函数。
proxy.ServeHTTP(c.Writer, c.Request)
}

// 创建一个httputil.ReverseProxy类型的代理对象,并设置其属性,将请求转发到目标URL
// NewSingleHostReverseProxy的参数是一个指向URL结构体的指针,用于指定目标URL。
proxy := httputil.NewSingleHostReverseProxy(remote)
// Director属性是一个函数,该函数用于修改请求的属性,例如修改请求头、请求路径等。
// 该函数的第一个参数是一个指向http.Request类型的指针,用于获取请求的属性。
// 函数体中将请求头、请求路径等属性修改为目标URL的属性。
proxy.Director = func(req *http.Request) {
// Header属性是一个map,用于存储请求头(Headers)
req.Header = c.Request.Header
// Host属性是请求头中的Host字段,用于指定请求的主机名(模块名称.default.域名),会解析为IP地址。
req.Host = remote.Host
// Scheme属性是请求头中的Scheme字段,用于指定请求的协议(http、https)
req.URL.Scheme = remote.Scheme
// URL.Host属性是请求头中的Host字段,用于指定请求的主机名(模块名称.default.域名)
req.URL.Host = remote.Host
// URL.Path属性是请求头中的Path字段,用于指定请求的路径。
req.URL.Path = realPath
}
// ServeHTTP方法用于将请求转发到目标URL
// 第一个参数是一个ResponseWriter类型的对象,用于将响应返回给客户端。
// 第二个参数是一个指向http.Request类型的指针,用于获取请求的属性,传递给Director函数。
proxy.ServeHTTP(c.Writer, c.Request)
}
Loading