package
1.17.10
Repository: https://github.com/ixre/gof.git
Documentation: pkg.go.dev

# README

HTTP API

接口通过HTTP暴露给外部应用,采用签名鉴权,接口请求方需要使用KEY和SECRET方可调用>接口,SECRET用于参数签名。

接口测试数据

    接口地址:http://localhost:7020/api
    接口KEY:test
    接口SECRET: 123456

接口签名规则

接口采用POST请求,将参数集合按字母排序后,排除sign_type,拼接token 然后进行MD5或SHA1进行加密得到sign,并将sign添加到请求参数集合。

签名示例代码(go):

// 参数排序后,排除sign和sign_type,拼接token,转换为字节
func paramsToBytes(r url.Values, token string) []byte {
    i := 0
    buf := bytes.NewBuffer(nil)
    // 键排序
    keys := []string{}
    for k, _ := range r {
	    keys = append(keys, k)
    }
    sort.Strings(keys)
    // 拼接参数和值
    for _, k := range keys {
	    if k == "sign" || k == "sign_type" {
		    continue
	    }
	    if i > 0 {
		    buf.WriteString("&")
	    }
	    buf.WriteString(k)
	    buf.WriteString("=")
	    buf.WriteString(r[k][0])
	    i++
    }
    buf.WriteString(token)
    return buf.Bytes()
}

// 签名
func Sign(signType string, r url.Values, token string) string {
    data := paramsToBytes(r, token)
    switch signType {
    case "md5":
	    return md5Encode(data)
    case "sha1":
	    return sha1Encode(data)
    }
    return ""
}

// MD5加密
func md5Encode(data []byte) string {
    m := md5.New()
    m.Write(data)
    dec := m.Sum(nil)
    return hex.EncodeToString(dec)
}
// SHA1加密
func sha1Encode(data []byte) string {
    s := sha1.New()
    s.Write(data)
    d := s.Sum(nil)
    return hex.EncodeToString(d)
}

接口返回

接口返回错误格式为:"#错误码#错误消息",如:

#10091#api access denied

接口请求示例代码(go)

key := "test"
secret := "123456"
signType := "sha1"
serverUrl := "http://localhost:7020/api"
form := url.Values{
    "key":  []string{key},
    "api":       []string{"status.ping,status.hello"},
    "sign_type": []string{signType},
}
sign := Sign(signType, form, secret)
// sign = "fe343c958b61178b3644432263cf819c153569ed"
form["sign"] = []string{sign}
cli := http.Client{}
rsp, err := cli.PostForm(serverUrl, form)
if err == nil {	
    data, _ := io.ReadAll(rsp.Body)
    log.Println("接口响应:", string(data))
}

创建服务

创建处理程序

var _ api.Handler = new(MemberApi)
type MemberApi struct {}

func (m MemberApi) Process(fn string, ctx api.Context) *api.Response {
	return api.HandleMultiFunc(fn,ctx,map[string]api.HandlerFunc{
		"login":m.login,
	})
}

func (m MemberApi) login(ctx api.Context)interface{}{
	return 1
}

创建服务

// 服务
func NewServe(debug bool,version string) http.Handler {
	// 初始化变量
	registry := map[string]interface{}{}
	// 创建上下文工厂
	factory := api.DefaultFactory.Build(registry)
	serve := NewService(factory, version, debug)
	// 创建http处理器
	hs := http.NewServeMux()
	hs.Handle("/api", serve)
	return hs
}


// 服务
func NewService(factory api.ContextFactory, ver string, debug bool) *api.ServeMux {
	// 创建服务
	s := api.NewServerMux(factory, swapApiKeyFunc)
	// 注册处理器
	s.Register("member", &MemberApi{})
	//s.Register("dept", &DeptApi{})
	//s.Register("role", &RoleApi{})
	//s.Register("res", &ResApi{})
	//s.Register("user", &UserApi{})
	// 注册中间键
	serviceMiddleware(s, "[ Go2o][ API][ Log]: ", ver, debug)
	return s
}

// 服务调试跟踪
func serviceMiddleware(s api.Server, prefix string, tarVer string, debug bool) {
	prefix = "[ Api][ Log]"
	if debug {
		// 开启调试
		s.Trace()
		// 输出请求信息
		s.Use(func(ctx api.Context) error {
			apiName := ctx.Form().Get("$api").(string)
			log.Println(prefix, "user", ctx.Key(), " calling ", apiName)
			data, _ := url.QueryUnescape(ctx.Request().Form.Encode())
			log.Println(prefix, "request data = [", data, "]")
			// 记录服务端请求时间
			ctx.Form().Set("$rpc_begin_time", time.Now().UnixNano())
			return nil
		})
	}
	// 校验版本
	s.Use(func(ctx api.Context) error {
		//prod := ctx.FormData().GetString("product")
		prodVer := ctx.Form().GetString("version")
		if api.CompareVersion(prodVer, tarVer) < 0 {
			return errors.New(fmt.Sprintf("%s,require version=%s",
				api.RDeprecated.Message, tarVer))
		}
		return nil
	})

	if debug {
		// 输出响应结果
		s.After(func(ctx api.Context) error {
			form := ctx.Form()
			rsp := form.Get("$api_response").(*api.Response)
			data := ""
			if rsp.Data != nil {
				d, _ := json.Marshal(rsp.Data)
				data = string(d)
			}
			reqTime := int64(ctx.Form().GetInt("$rpc_begin_time"))
			elapsed := float32(time.Now().UnixNano()-reqTime) / 1000000000
			log.Println(prefix, "response : ", rsp.Code, rsp.Message,
				fmt.Sprintf("; elapsed time :%.4fs ; ", elapsed),
				"result = [", data, "]",
			)
			if rsp.Code == api.RAccessDenied.Code {
				data, _ := url.QueryUnescape(ctx.Request().Form.Encode())
				sortData := api.ParamsToBytes(ctx.Request().Form, form.GetString("$user_secret"))
				log.Println(prefix, "request data = [", data, "]")
				log.Println(" sign not match ! key =", form.Get("key"),
					"\r\n   server_sign=", form.GetString("$server_sign"),
					"\r\n   client_sign=", form.GetString("$client_sign"),
					"\r\n   sort_params=", string(sortData))
			}
			return nil
		})
	}
}

// 交换接口用户凭据
func swapApiKeyFunc(ctx api.Context, key string) (userId int, userSecret string) {
	if key == "go2o"{
		return 1,"131409"
	}
	//log.Println(fmt.Sprintf("[ UAMS][ API]: 接口用户[%s]交换凭据失败: %s", key, r.ErrMsg))
	return 0, ""
}

# Functions

生成访问token.
No description provided by the author
创建凭据.
处理接口方法.
No description provided by the author
创建新的客户端.
No description provided by the author
No description provided by the author
参数首字母小写后排序,排除sign和sign_type,secret,转换为字节.
No description provided by the author
签名.

# Variables

access denied.
api is deprecated.
server internal error.
access token is invalid.
not authorized.
access token is expired.
api not defined.

# Structs

No description provided by the author
No description provided by the author
接口响应.
No description provided by the author
default server implement.

# Interfaces

上下文.
接口处理器.
api server.

# Type aliases

fetch access token function.
No description provided by the author
错误响应处理函数.
接口处理方法.
No description provided by the author
中间件.
数据.
获取用户私钥,返回错误后将直接输出到客户端.