GO JSSDK 前后端服务的实现

185 min read
package jssdk

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

var appid string
var appsecret string

type Wxconfig struct {
	appId     string
	appSecret string
	url       string
}
type json1 struct {
	Expire_time  int
	Jsapi_ticket string
}

type json2 struct {
	Expire_time  int
	Access_token string
}
type token struct {
	Access_token string
	Expires_in   int
}

type ticket struct {
	Errcode    int
	Errmsg     string
	Ticket     string
	Expires_in int
}

type signPackage struct {
	AppId     string   `json:"appId"`
	NonceStr  string   `json:"nonceStr"`
	Timestamp int      `json:"timestamp"`
	Url       string   `json:"url"`
	Signature string   `json:"signature"`
	JsApiList []string `json:"jsApiList"`
}

func New(appId string, appSecret string, url string) Wxconfig {
	wx := Wxconfig{appId, appSecret, url}
	return wx
}

// GetWechatConfig 创建类方法获取jssdk配置项目 返回json串
func (wx Wxconfig) GetWechatConfig() signPackage {
	appid = wx.appId
	appsecret = wx.appSecret
	fmt.Printf("running appid: %s appsecret: %s\n", wx.appId, wx.appSecret)
	ticket := getJsApiTicket()
	timestamp := timestamp()
	fmt.Printf("timestamp%d", timestamp)
	nonceStr := getRandomString(16)
	fmt.Printf("nonceStr:%s", nonceStr)
	url := wx.url
	myString := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s", ticket, nonceStr, timestamp, url)
	fmt.Printf("myString:%s", myString)
	signature := mySha1(myString)
	s := signPackage{wx.appId, nonceStr, timestamp, url, signature, []string{"onMenuShareTimeline", "onMenuShareAppMessage"}}
	//fmt.Println(signPackage)
	//data, err := json.Marshal(signPackage)
	//if err != nil {
	//	fmt.Println(err)
	//}
	return s
}

// 函数首字母小写 表示私有
func getJsApiTicket() string {
	fmt.Printf("获取getJsApiTicket\n")
	// 获取ticket内容
	jsonStr := readFile("ticket.txt")
	// 解析json串
	j := json1{}
	err := json.Unmarshal([]byte(jsonStr), &j)
	if err != nil {
		fmt.Println(err.Error())
		return ""
	}
	expireTime := j.Expire_time
	jsapiTicket := j.Jsapi_ticket
	if expireTime < timestamp() {
		// ticket过期需要从新获取
		// 获取access_token生成ticket
		accessToken := getAccessToken()
		fmt.Println(accessToken)
		if accessToken == "" {
			return ""
		}
		url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=%s", accessToken)
		resp, _ := http.Get(url)

		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		// 解析body
		ticket := ticket{}
		err := json.Unmarshal(body, &ticket)
		if err != nil {
			fmt.Println(err.Error())
			return ""
		}
		if ticket.Errcode == 0 {
			jj := json1{timestamp() + 7000, ticket.Ticket}
			data, err := json.Marshal(jj)
			if err != nil {
				fmt.Println(err)
			}
			setFile(string(data), "ticket.txt")
			return ticket.Ticket
		} else {
			return ""
		}
	}

	return jsapiTicket
}

// 写文件"./jssdk/ticket.txt"
func setFile(ticket string, filename string) bool {
	d1 := []byte(ticket)
	err := ioutil.WriteFile(filename, d1, 0644)
	if err != nil {
		fmt.Println(err)
		return false
	}
	return true
}

// 读取文件内容
func readFile(filename string) string {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("文件读取出错", err)
		return fmt.Sprintf("%s", err)
	}
	fmt.Println("文件读取成功", string(data))
	return string(data)
}

// 获取access token
func getAccessToken() string {
	// 获取ticket内容
	jsonStr := readFile("token.txt")
	// 解析json串
	j := json2{}
	err := json.Unmarshal([]byte(jsonStr), &j)
	if err != nil {
		fmt.Println(err.Error())
		return ""
	}
	expireTime := j.Expire_time
	accessToken := j.Access_token
	if expireTime < timestamp() {
		// 获取新token
		url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appid, appsecret)
		resp, _ := http.Get(url)

		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		// 解析body
		token := token{}
		err := json.Unmarshal([]byte(body), &token)
		if err != nil {
			fmt.Println(err.Error())
			return ""
		}
		if len(token.Access_token) > 0 {
			jj := json2{timestamp() + 7000, token.Access_token}
			data, err := json.Marshal(jj)
			if err != nil {
				fmt.Println(err)
			}
			setFile(string(data), "token.txt")
			return token.Access_token
		} else {
			return ""
		}
	}
	return accessToken
}

func timestamp() int {
	timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10)
	myTimestamp, _ := strconv.Atoi(timestamp)
	return myTimestamp
}

// 获取随机字符串
func getRandomString(l int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	bytes := []byte(str)
	var result []byte
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < l; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

// sha1加密
func mySha1(data string) string {
	t := sha1.New()
	_, err := io.WriteString(t, data)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", t.Sum(nil))
}

接口层

package main

import (
	"github.com/gin-gonic/gin"
	"go-jssdk/jssdk"
)

func main() {
	r := gin.Default()
	r.Use(Cors()) // 主要是这里
	r.GET("/jssdk", func(c *gin.Context) {
		url := c.DefaultQuery("url", "url")
		jssdk := jssdk.New("wx74xxxxxx80f5a445", "c2e72ee嘻嘻嘻嘻嘻嘻dc3dc594", url)
		config := jssdk.GetWechatConfig()
		c.JSON(200, gin.H{
			"message": "ok",
			"data":    config,
		})
	})

	r.Run(":52446")
}

// Cors 开启跨域函数
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Origin")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		defer func() {
			if err := recover(); err != nil {

			}
		}()

		c.Next()
	}
}

前端代码

引用jssdk,操他妈的马化腾 张小龙,整了个什么啥逼玩意,狗屁玩意, 别用 1.6.0,别用 1.6.0,别用 1.6.0,别用 1.6.0

 <script src="http://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
function share(article) {
  let url = location.href.split("#")[0];
  axios
    .get("http://微信狗东西去死:52446/jssdk?url=" + encodeURIComponent(url))
    .then(function (response) {
      let msg = response.data.data;
      wx.config({
        debug: false,
        appId: msg.appId,
        timestamp: msg.timestamp,
        nonceStr: msg.nonceStr,
        signature: msg.signature,
        jsApiList: [
          "updateAppMessageShareData",
          "updateTimelineShareData",
          "onMenuShareAppMessage",
          "onMenuShareTimeline",
          "onMenuShareQQ",
          "onMenuShareQZone",
        ],
      });
      //处理验证失败的信息
      wx.error(function (res) {
        console.log("res", res);
      });

      wx.ready(function () {
 
        wx.onMenuShareAppMessage({
          title: article.title,
          desc: article.intro ? article.intro : "",
          link: url,
          imgUrl:
            "http://img.谢谢谢谢谢.com/2021/01/22/张小龙全家死光.jpg",
        });

        wx.onMenuShareTimeline({
          title: article.title,
          desc: article.intro ? article.intro : "",
          link: url,
          imgUrl:
            "http://img.谢谢谢谢谢.com/2021/01/22/一天操一遍马化腾奶奶.jpg",
        });
      });
    });
}