Skip to content

Commit 93b8dd4

Browse files
author
xingwang.liu
committed
[feat] 1. 增加自定义签名实现,可选配置项
1 parent 8743791 commit 93b8dd4

File tree

6 files changed

+204
-125
lines changed

6 files changed

+204
-125
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ go get -u github.com/shima-park/agollo
2626
* 客户端SLB
2727
* 提供Viper配置库的apollo插件
2828
* 支持通过 ACCESSKEY_SECRET 来实现 client 安全访问
29+
* 支持自定义签名认证
2930

3031
## 示例
3132

apollo.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package agollo
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// https://github.com/ctripcorp/apollo/wiki/%E5%85%B6%E5%AE%83%E8%AF%AD%E8%A8%80%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
8+
type ApolloClient interface {
9+
Apply(opts ...ApolloClientOption)
10+
11+
Notifications(configServerURL, appID, clusterName string, notifications []Notification) (int, []Notification, error)
12+
13+
// 该接口会直接从数据库中获取配置,可以配合配置推送通知实现实时更新配置。
14+
GetConfigsFromNonCache(configServerURL, appID, cluster, namespace string, opts ...NotificationsOption) (int, *Config, error)
15+
16+
// 该接口会从缓存中获取配置,适合频率较高的配置拉取请求,如简单的每30秒轮询一次配置。
17+
GetConfigsFromCache(configServerURL, appID, cluster, namespace string) (Configurations, error)
18+
19+
// 该接口从MetaServer获取ConfigServer列表
20+
GetConfigServers(metaServerURL, appID string) (int, []ConfigServer, error)
21+
}
22+
23+
type Notifications []Notification
24+
25+
func (n Notifications) String() string {
26+
bytes, _ := json.Marshal(n)
27+
return string(bytes)
28+
}
29+
30+
type Notification struct {
31+
NamespaceName string `json:"namespaceName"` // namespaceName: "application",
32+
NotificationID int `json:"notificationId"` // notificationId: 107
33+
}
34+
35+
type NotificationsOptions struct {
36+
ReleaseKey string
37+
}
38+
39+
type NotificationsOption func(*NotificationsOptions)
40+
41+
func ReleaseKey(releaseKey string) NotificationsOption {
42+
return func(o *NotificationsOptions) {
43+
o.ReleaseKey = releaseKey
44+
}
45+
}
46+
47+
type Config struct {
48+
AppID string `json:"appId"` // appId: "AppTest",
49+
Cluster string `json:"cluster"` // cluster: "default",
50+
NamespaceName string `json:"namespaceName"` // namespaceName: "TEST.Namespace1",
51+
Configurations Configurations `json:"configurations"` // configurations: {Name: "Foo"},
52+
ReleaseKey string `json:"releaseKey"` // releaseKey: "20181017110222-5ce3b2da895720e8"
53+
}
54+
55+
type ConfigServer struct {
56+
AppName string `json:"appName"`
57+
InstanceID string `json:"instanceId"`
58+
HomePageURL string `json:"homepageUrl"`
59+
}

apollo_client.go

Lines changed: 41 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,36 @@
11
package agollo
22

33
import (
4-
"crypto/hmac"
5-
"crypto/sha1"
6-
"encoding/base64"
74
"encoding/json"
85
"fmt"
96
"io/ioutil"
107
"net"
118
"net/http"
129
"net/url"
10+
"os"
1311
"time"
1412
)
1513

1614
var (
1715
defaultClientTimeout = 90 * time.Second
1816
)
1917

20-
// https://github.com/ctripcorp/apollo/wiki/%E5%85%B6%E5%AE%83%E8%AF%AD%E8%A8%80%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
21-
type ApolloClient interface {
22-
Apply(opts ...ApolloClientOption)
23-
24-
Notifications(configServerURL, appID, clusterName string, notifications []Notification) (int, []Notification, error)
25-
26-
// 该接口会直接从数据库中获取配置,可以配合配置推送通知实现实时更新配置。
27-
GetConfigsFromNonCache(configServerURL, appID, cluster, namespace string, opts ...NotificationsOption) (int, *Config, error)
28-
29-
// 该接口会从缓存中获取配置,适合频率较高的配置拉取请求,如简单的每30秒轮询一次配置。
30-
GetConfigsFromCache(configServerURL, appID, cluster, namespace string) (Configurations, error)
31-
32-
// 该接口从MetaServer获取ConfigServer列表
33-
GetConfigServers(metaServerURL, appID string) (int, []ConfigServer, error)
34-
}
35-
36-
type Notifications []Notification
37-
38-
func (n Notifications) String() string {
39-
bytes, _ := json.Marshal(n)
40-
return string(bytes)
41-
}
42-
43-
type Notification struct {
44-
NamespaceName string `json:"namespaceName"` // namespaceName: "application",
45-
NotificationID int `json:"notificationId"` // notificationId: 107
46-
}
47-
48-
type NotificationsOptions struct {
49-
ReleaseKey string
50-
}
51-
52-
type NotificationsOption func(*NotificationsOptions)
53-
54-
func ReleaseKey(releaseKey string) NotificationsOption {
55-
return func(o *NotificationsOptions) {
56-
o.ReleaseKey = releaseKey
57-
}
58-
}
59-
60-
type Config struct {
61-
AppID string `json:"appId"` // appId: "AppTest",
62-
Cluster string `json:"cluster"` // cluster: "default",
63-
NamespaceName string `json:"namespaceName"` // namespaceName: "TEST.Namespace1",
64-
Configurations Configurations `json:"configurations"` // configurations: {Name: "Foo"},
65-
ReleaseKey string `json:"releaseKey"` // releaseKey: "20181017110222-5ce3b2da895720e8"
66-
}
67-
68-
type ConfigServer struct {
69-
AppName string `json:"appName"`
70-
InstanceID string `json:"instanceId"`
71-
HomePageURL string `json:"homepageUrl"`
72-
}
18+
const (
19+
// ENV_APOLLO_ACCESS_KEY 默认从环境变量中读取Apollo的AccessKey
20+
// 会被显示传入的AccessKey所覆盖
21+
ENV_APOLLO_ACCESS_KEY = "APOLLO_ACCESS_KEY"
22+
)
7323

7424
type Doer interface {
7525
Do(*http.Request) (*http.Response, error)
7626
}
7727

7828
type apolloClient struct {
79-
Doer Doer
80-
IP string
81-
ConfigType string // 默认properties不需要在namespace后加后缀名,其他情况例如application.json {xml,yml,yaml,json,...}
82-
AccessKey string
83-
}
84-
85-
type ApolloClientOption func(*apolloClient)
86-
87-
func WithDoer(d Doer) ApolloClientOption {
88-
return func(a *apolloClient) {
89-
a.Doer = d
90-
}
91-
}
92-
93-
func WithIP(ip string) ApolloClientOption {
94-
return func(a *apolloClient) {
95-
a.IP = ip
96-
}
97-
}
98-
99-
func WithConfigType(configType string) ApolloClientOption {
100-
return func(a *apolloClient) {
101-
a.ConfigType = configType
102-
}
103-
}
104-
105-
func WithAccessKey(accessKey string) ApolloClientOption {
106-
return func(a *apolloClient) {
107-
a.AccessKey = accessKey
108-
}
29+
Doer Doer
30+
IP string
31+
ConfigType string // 默认properties不需要在namespace后加后缀名,其他情况例如application.json {xml,yml,yaml,json,...}
32+
AccessKey string
33+
SignatureFunc SignatureFunc
10934
}
11035

11136
func NewApolloClient(opts ...ApolloClientOption) ApolloClient {
@@ -115,46 +40,15 @@ func NewApolloClient(opts ...ApolloClientOption) ApolloClient {
11540
Doer: &http.Client{
11641
Timeout: defaultClientTimeout, // Notifications由于服务端会hold住请求60秒,所以请确保客户端访问服务端的超时时间要大于60秒。
11742
},
43+
AccessKey: os.Getenv(ENV_APOLLO_ACCESS_KEY),
44+
SignatureFunc: DefaultSignatureFunc,
11845
}
11946

12047
c.Apply(opts...)
12148

12249
return c
12350
}
12451

125-
const (
126-
AUTHORIZATION_FORMAT = "Apollo %s:%s"
127-
DELIMITER = "\n"
128-
HTTP_HEADER_AUTHORIZATION = "Authorization"
129-
HTTP_HEADER_TIMESTAMP = "Timestamp"
130-
)
131-
132-
func signature(timestamp, url, accessKey string) string {
133-
134-
stringToSign := timestamp + DELIMITER + url
135-
136-
key := []byte(accessKey)
137-
mac := hmac.New(sha1.New, key)
138-
_, _ = mac.Write([]byte(stringToSign))
139-
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
140-
}
141-
142-
func (c *apolloClient) httpHeader(appID, uri string) map[string]string {
143-
144-
headers := map[string]string{}
145-
if "" == c.AccessKey {
146-
return headers
147-
}
148-
149-
timestamp := fmt.Sprintf("%v", time.Now().UnixNano()/int64(time.Millisecond))
150-
signature := signature(timestamp, uri, c.AccessKey)
151-
152-
headers[HTTP_HEADER_AUTHORIZATION] = fmt.Sprintf(AUTHORIZATION_FORMAT, appID, signature)
153-
headers[HTTP_HEADER_TIMESTAMP] = timestamp
154-
155-
return headers
156-
}
157-
15852
func (c *apolloClient) Apply(opts ...ApolloClientOption) {
15953
for _, opt := range opts {
16054
opt(c)
@@ -170,7 +64,13 @@ func (c *apolloClient) Notifications(configServerURL, appID, cluster string, not
17064
)
17165
apiURL := fmt.Sprintf("%s%s", configServerURL, requestURI)
17266

173-
headers := c.httpHeader(appID, requestURI)
67+
headers := c.SignatureFunc(&SignatureContext{
68+
ConfigServerURL: configServerURL,
69+
RequestURI: requestURI,
70+
AccessKey: c.AccessKey,
71+
AppID: appID,
72+
Cluster: cluster,
73+
})
17474
status, err = c.do("GET", apiURL, headers, &result)
17575
return
17676
}
@@ -191,7 +91,13 @@ func (c *apolloClient) GetConfigsFromNonCache(configServerURL, appID, cluster, n
19191
)
19292
apiURL := fmt.Sprintf("%s%s", configServerURL, requestURI)
19393

194-
headers := c.httpHeader(appID, requestURI)
94+
headers := c.SignatureFunc(&SignatureContext{
95+
ConfigServerURL: configServerURL,
96+
RequestURI: requestURI,
97+
AccessKey: c.AccessKey,
98+
AppID: appID,
99+
Cluster: cluster,
100+
})
195101
config = new(Config)
196102
status, err = c.do("GET", apiURL, headers, config)
197103
return
@@ -208,7 +114,13 @@ func (c *apolloClient) GetConfigsFromCache(configServerURL, appID, cluster, name
208114
)
209115
apiURL := fmt.Sprintf("%s%s", configServerURL, requestURI)
210116

211-
headers := c.httpHeader(appID, requestURI)
117+
headers := c.SignatureFunc(&SignatureContext{
118+
ConfigServerURL: configServerURL,
119+
RequestURI: requestURI,
120+
AccessKey: c.AccessKey,
121+
AppID: appID,
122+
Cluster: cluster,
123+
})
212124
config = make(Configurations)
213125
_, err = c.do("GET", apiURL, headers, config)
214126
return
@@ -219,7 +131,13 @@ func (c *apolloClient) GetConfigServers(metaServerURL, appID string) (int, []Con
219131
requestURI := fmt.Sprintf("/services/config?id=%s&appId=%s", c.IP, appID)
220132
apiURL := fmt.Sprintf("%s%s", metaServerURL, requestURI)
221133

222-
headers := c.httpHeader(appID, requestURI)
134+
headers := c.SignatureFunc(&SignatureContext{
135+
ConfigServerURL: metaServerURL,
136+
RequestURI: requestURI,
137+
AccessKey: c.AccessKey,
138+
AppID: appID,
139+
Cluster: "",
140+
})
223141
var cfs []ConfigServer
224142
status, err := c.do("GET", apiURL, headers, &cfs)
225143
return status, cfs, err

apollo_client_options.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package agollo
2+
3+
type ApolloClientOption func(*apolloClient)
4+
5+
func WithDoer(d Doer) ApolloClientOption {
6+
return func(a *apolloClient) {
7+
a.Doer = d
8+
}
9+
}
10+
11+
func WithIP(ip string) ApolloClientOption {
12+
return func(a *apolloClient) {
13+
a.IP = ip
14+
}
15+
}
16+
17+
func WithConfigType(configType string) ApolloClientOption {
18+
return func(a *apolloClient) {
19+
a.ConfigType = configType
20+
}
21+
}
22+
23+
func WithAccessKey(accessKey string) ApolloClientOption {
24+
return func(a *apolloClient) {
25+
a.AccessKey = accessKey
26+
}
27+
}
28+
29+
func WithSignatureFunc(sf SignatureFunc) ApolloClientOption {
30+
return func(a *apolloClient) {
31+
a.SignatureFunc = sf
32+
}
33+
}

apollo_client_signature.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package agollo
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha1"
6+
"encoding/base64"
7+
"fmt"
8+
"time"
9+
)
10+
11+
const (
12+
AUTHORIZATION_FORMAT = "Apollo %s:%s"
13+
DELIMITER = "\n"
14+
HTTP_HEADER_AUTHORIZATION = "Authorization"
15+
HTTP_HEADER_TIMESTAMP = "Timestamp"
16+
)
17+
18+
type Header map[string]string
19+
20+
type SignatureContext struct {
21+
ConfigServerURL string // 当前访问配置使用的apollo config server的url
22+
AccessKey string // 服务启动时缓存的access key
23+
AppID string // appid
24+
RequestURI string // 请求的uri,domain之后的路径
25+
Cluster string // 请求的集群,默认情况下: default,请求GetConfigServers接口时为""
26+
}
27+
28+
type SignatureFunc func(ctx *SignatureContext) Header
29+
30+
func DefaultSignatureFunc(ctx *SignatureContext) Header {
31+
32+
headers := map[string]string{}
33+
if "" == ctx.AccessKey {
34+
return headers
35+
}
36+
37+
timestamp := fmt.Sprintf("%v", time.Now().UnixNano()/int64(time.Millisecond))
38+
signature := signature(timestamp, ctx.RequestURI, ctx.AccessKey)
39+
40+
headers[HTTP_HEADER_AUTHORIZATION] = fmt.Sprintf(AUTHORIZATION_FORMAT, ctx.AppID, signature)
41+
headers[HTTP_HEADER_TIMESTAMP] = timestamp
42+
43+
return headers
44+
}
45+
46+
func signature(timestamp, url, accessKey string) string {
47+
48+
stringToSign := timestamp + DELIMITER + url
49+
50+
key := []byte(accessKey)
51+
mac := hmac.New(sha1.New, key)
52+
_, _ = mac.Write([]byte(stringToSign))
53+
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
54+
}

0 commit comments

Comments
 (0)