From d9075933c1a0f0a8d01657ba6cefbd4b994ce640 Mon Sep 17 00:00:00 2001 From: wenzl Date: Sun, 11 Sep 2016 13:16:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0access=5Ftoken=E8=8E=B7?= =?UTF-8?q?=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/cache.go | 11 +++++++ cache/memcache.go | 51 +++++++++++++++++++++++++++++ cache/memcache_test.go | 28 ++++++++++++++++ context/access_token.go | 71 +++++++++++++++++++++++++++++++++++++++++ context/context.go | 14 +++++--- util/http.go | 19 +++++++++++ wechat.go | 5 +++ 7 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 cache/cache.go create mode 100644 cache/memcache.go create mode 100644 cache/memcache_test.go create mode 100644 context/access_token.go create mode 100644 util/http.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..614999f --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,11 @@ +package cache + +import "time" + +//Cache interface +type Cache interface { + Get(key string) interface{} + Set(key string, val interface{}, timeput time.Duration) error + IsExist(key string) bool + Delete(key string) error +} diff --git a/cache/memcache.go b/cache/memcache.go new file mode 100644 index 0000000..010b2c6 --- /dev/null +++ b/cache/memcache.go @@ -0,0 +1,51 @@ +package cache + +import ( + "errors" + "time" + + "github.com/bradfitz/gomemcache/memcache" +) + +//Memcache struct contains *memcache.Client +type Memcache struct { + conn *memcache.Client +} + +//NewMemcache create new memcache +func NewMemcache(server ...string) *Memcache { + mc := memcache.New(server...) + return &Memcache{mc} +} + +//Get return cached value +func (mem *Memcache) Get(key string) interface{} { + if item, err := mem.conn.Get(key); err == nil { + return string(item.Value) + } + return nil +} + +// IsExist check value exists in memcache. +func (mem *Memcache) IsExist(key string) bool { + _, err := mem.conn.Get(key) + if err != nil { + return false + } + return true +} + +//Set cached value with key and expire time. +func (mem *Memcache) Set(key string, val interface{}, timeout time.Duration) error { + v, ok := val.(string) + if !ok { + return errors.New("val must string") + } + item := &memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout / time.Second)} + return mem.conn.Set(item) +} + +//Delete delete value in memcache. +func (mem *Memcache) Delete(key string) error { + return mem.conn.Delete(key) +} diff --git a/cache/memcache_test.go b/cache/memcache_test.go new file mode 100644 index 0000000..6b4ea02 --- /dev/null +++ b/cache/memcache_test.go @@ -0,0 +1,28 @@ +package cache + +import ( + "testing" + "time" +) + +func TestMemcache(t *testing.T) { + mem := NewMemcache("127.0.0.1:11211") + var err error + timeoutDuration := 10 * time.Second + if err = mem.Set("username", "silenceper", timeoutDuration); err != nil { + t.Error("set Error", err) + } + + if !mem.IsExist("username") { + t.Error("IsExist Error") + } + + name := mem.Get("username").(string) + if name != "silenceper" { + t.Error("get Error") + } + + if err = mem.Delete("username"); err != nil { + t.Errorf("delete Error , err=%v", err) + } +} diff --git a/context/access_token.go b/context/access_token.go new file mode 100644 index 0000000..ae55564 --- /dev/null +++ b/context/access_token.go @@ -0,0 +1,71 @@ +package context + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/silenceper/wechat/util" +) + +const ( + //AccessTokenURL 获取access_token的接口 + AccessTokenURL = "https://api.weixin.qq.com/cgi-bin/token" +) + +//ResAccessToken struct +type ResAccessToken struct { + ErrorCode int32 `json:"errcode"` + ErrorMsg string `json:"errmsg"` + + AccessToken string `json:"access_token"` + ExpiresIn int32 `json:"expires_in"` +} + +//SetAccessTokenLock 设置读写锁(一个appID一个读写锁) +func (ctx *Context) SetAccessTokenLock(l *sync.RWMutex) { + ctx.accessTokenLock = l +} + +//GetAccessToken 获取access_token +func (ctx *Context) GetAccessToken() (accessToken string, err error) { + ctx.accessTokenLock.Lock() + defer ctx.accessTokenLock.Unlock() + + accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID) + val := ctx.Cache.Get(accessTokenCacheKey) + if val != nil { + accessToken = val.(string) + return + } + + //从微信服务器获取 + var resAccessToken ResAccessToken + resAccessToken, err = ctx.GetAccessTokenFromServer() + if err != nil { + return + } + + accessToken = resAccessToken.AccessToken + return +} + +//GetAccessTokenFromServer 强制从微信服务器获取token +func (ctx *Context) GetAccessTokenFromServer() (resAccessToken ResAccessToken, err error) { + url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, ctx.AppID, ctx.AppSecret) + var body []byte + body, err = util.HTTPGet(url) + err = json.Unmarshal(body, &resAccessToken) + if err != nil { + return + } + if resAccessToken.ErrorMsg != "" { + err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrorCode, resAccessToken.ErrorMsg) + return + } + + accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID) + err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(resAccessToken.ExpiresIn)-1500/time.Second) + return +} diff --git a/context/context.go b/context/context.go index 82288ae..75bd4a8 100644 --- a/context/context.go +++ b/context/context.go @@ -1,6 +1,11 @@ package context -import "net/http" +import ( + "net/http" + "sync" + + "github.com/silenceper/wechat/cache" +) //Context struct type Context struct { @@ -9,12 +14,13 @@ type Context struct { Token string EncodingAESKey string + Cache cache.Cache + Writer http.ResponseWriter Request *http.Request -} - -func (ctx *Context) getAccessToken() { + //accessTokenLock 读写锁 同一个AppID一个 + accessTokenLock *sync.RWMutex } // Query returns the keyed url query value if it exists diff --git a/util/http.go b/util/http.go new file mode 100644 index 0000000..203fa4a --- /dev/null +++ b/util/http.go @@ -0,0 +1,19 @@ +package util + +import ( + "io/ioutil" + "net/http" +) + +//HTTPGet get 请求 +func HTTPGet(url string) ([]byte, error) { + response, err := http.Get(url) + if err != nil { + return nil, err + } + return ioutil.ReadAll(response.Body) +} + +//HTTPPost post 请求 +func HTTPPost() { +} diff --git a/wechat.go b/wechat.go index c81d562..1067169 100644 --- a/wechat.go +++ b/wechat.go @@ -2,7 +2,9 @@ package wechat import ( "net/http" + "sync" + "github.com/silenceper/wechat/cache" "github.com/silenceper/wechat/context" "github.com/silenceper/wechat/server" ) @@ -18,6 +20,7 @@ type Config struct { AppSecret string Token string EncodingAESKey string + Cache cache.Cache } //NewWechat init @@ -32,6 +35,8 @@ func copyConfigToContext(cfg *Config, context *context.Context) { context.AppSecret = cfg.AppSecret context.Token = cfg.Token context.EncodingAESKey = cfg.EncodingAESKey + context.Cache = cfg.Cache + context.SetAccessTokenLock(new(sync.RWMutex)) } //GetServer init