go语言实现微信自定义分享签名的获取

微信自定义分享签名的获取步骤:

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/

你可能感兴趣的:(Go)