微信自定义分享签名的获取步骤:
1) 获取access_token(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183);
2) 获取jsapi_ticket;
3) 计算signature(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 附录一)。
注意事项:
1) 需要一个可以用的微信公众号,目的是为了获取AppID和AppSecret;
2) 调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,否则会报错invalid ip not in whitelist hint;
3) 获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存access_token和jsapi_ticket。
代码实现
package main
import (
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"io/ioutil"
mathRand "math/rand"
"net/http"
"strconv"
"sync"
"time"
)
var (
MemoryCacheVar *MemoryCache
AppID string = "你的AppID"
AppSecret string = "你的AppSecret"
AccessTokenHost string = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppID + "&secret=" + AppSecret
JsAPITicketHost string = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"
)
func main() {
MemoryCacheVar = new(MemoryCache)
MemoryCacheVar.Items = make(map[string]*Item)
http.HandleFunc("/", getWxSign)
err:=http.ListenAndServe("127.0.0.1:5678", nil)
if err !=nil {
fmt.Println(err.Error())
}
}
func getWxSign(w http.ResponseWriter, r *http.Request) {
var (
noncestr, jsapi_ticket, timestamp, url, signature, signatureStr, access_token string
wxAccessToken WxAccessToken
wxJsApiTicket WxJsApiTicket
wxSignature WxSignature
wxSignRtn WxSignRtn
)
query := r.URL.Query()
url = query["url"][0]
noncestr = RandStringBytes(16)
timestamp = strconv.FormatInt(time.Now().Unix(), 10)
//获取access_token,如果缓存中有,则直接取出数据使用;否则重新调用微信端接口获取
client := &http.Client{}
if MemoryCacheVar.Get("access_token") == nil {
request, _ := http.NewRequest("GET", AccessTokenHost, nil)
response, _ := client.Do(request)
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
wxSignRtn.Code = 1
wxSignRtn.Msg = err.Error()
fmt.Fprintln(w,wxSignRtn)
return
}
err = json.Unmarshal(body, &wxAccessToken)
if err != nil {
wxSignRtn.Code = 1
wxSignRtn.Msg = err.Error()
fmt.Fprintln(w,wxSignRtn)
return
}
if wxAccessToken.Errcode == 0 {
access_token = wxAccessToken.Access_token
} else {
wxSignRtn.Code = 1
wxSignRtn.Msg = wxAccessToken.Errmsg
fmt.Fprintln(w,wxSignRtn)
return
}
MemoryCacheVar.Put("access_token", access_token, time.Duration(wxAccessToken.Expires_in)*time.Second)
//获取 jsapi_ticket
requestJs, _ := http.NewRequest("GET", JsAPITicketHost+"?access_token="+access_token+"&type=jsapi", nil)
responseJs, _ := client.Do(requestJs)
defer responseJs.Body.Close()
bodyJs, err := ioutil.ReadAll(responseJs.Body)
if err != nil {
wxSignRtn.Code = 1
wxSignRtn.Msg = err.Error()
fmt.Fprintln(w,wxSignRtn)
return
}
err = json.Unmarshal(bodyJs, &wxJsApiTicket)
if err != nil {
wxSignRtn.Code = 1
wxSignRtn.Msg = err.Error()
fmt.Fprintln(w,wxSignRtn)
return
}
if wxJsApiTicket.Errcode == 0 {
jsapi_ticket = wxJsApiTicket.Ticket
} else {
wxSignRtn.Code = 1
wxSignRtn.Msg = wxJsApiTicket.Errmsg
fmt.Fprintln(w,wxSignRtn)
return
}
MemoryCacheVar.Put("jsapi_ticket", jsapi_ticket, time.Duration(wxJsApiTicket.Expires_in)*time.Second)
} else {
//缓存中存在access_token,直接读取
access_token = MemoryCacheVar.Get("access_token").(*Item).Value
jsapi_ticket = MemoryCacheVar.Get("jsapi_ticket").(*Item).Value
}
fmt.Println("access_token:",access_token)
fmt.Println("jsapi_ticket:",jsapi_ticket)
// 获取 signature
signatureStr = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url
signature = GetSha1(signatureStr)
wxSignature.Url = url
wxSignature.Noncestr = noncestr
wxSignature.Timestamp = timestamp
wxSignature.Signature = signature
wxSignature.AppID = AppID
// 返回前端需要的数据
wxSignRtn.Code = 0
wxSignRtn.Msg = "success"
wxSignRtn.Data = wxSignature
fmt.Fprintln(w,wxSignRtn)
}
//生成指定长度的字符串
func RandStringBytes(n int) string {
const letterBytes = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[mathRand.Intn(len(letterBytes))]
}
return string(b)
}
//SHA1加密
func GetSha1(data string) string {
t := sha1.New()
io.WriteString(t, data)
return fmt.Sprintf("%x", t.Sum(nil))
}
type WxAccessToken struct {
Access_token string `json:"access_token"`
Expires_in int `json:"expires_in"`
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
type WxJsApiTicket struct {
Ticket string `json:"ticket"`
Expires_in int `json:"expires_in"`
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
type WxSignature struct {
Noncestr string `json:"noncestr"`
Timestamp string `json:"timestamp"`
Url string `json:"url"`
Signature string `json:"signature"`
AppID string `json:"appId"`
}
type WxSignRtn struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data WxSignature `json:"data"`
}
// 数据缓存处理
type Item struct {
Value string
CreateTime time.Time
LifeTime time.Duration
}
type MemoryCache struct {
sync.RWMutex
Items map[string]*Item
}
func (mc *MemoryCache) Put(key string, value string, lifeTime time.Duration) {
mc.Lock()
defer mc.Unlock()
mc.Items[key] = &Item{
LifeTime: lifeTime,
Value: value,
CreateTime: time.Now(),
}
}
func (mc *MemoryCache) Get(key string) interface{} {
mc.RLock()
defer mc.RUnlock()
if e, ok := mc.Items[key]; ok {
if !e.isExpire() {
return e
} else {
delete(mc.Items, key)
}
}
return nil
}
func (e *Item) isExpire() bool {
if e.LifeTime == 0 {
return false
}
//根据创建时间和生命周期判断元素是否失效
return time.Now().Sub(e.CreateTime) > e.LifeTime
}
浏览器输入网址:http://127.0.0.1:5678/?url=http://123.com,返回前端配置需要的数据,同时在服务端打印出access_token和jsapi_ticket,多调用几次,可以发现access_token和jsapi_ticke输出是一样的,是因为在服务端做了缓存。
参考文章:
http://www.duyongfeng.com/2017/12/16/go%E8%AF%AD%E8%A8%80%E4%B9%8B%E7%BC%93%E5%AD%98%E5%AE%9E%E7%8E%B0/