DelayNoMore/battle_srv/common/utils/wechat.go
2022-11-09 14:20:26 +08:00

278 lines
7.2 KiB
Go

package utils
import (
. "battle_srv/common"
. "battle_srv/configs"
"bytes"
"crypto/sha1"
. "dnmshared"
"encoding/json"
"fmt"
"go.uber.org/zap"
"io"
"io/ioutil"
"math/rand"
"net/http"
"sort"
"time"
)
var WechatIns *wechat
var WechatGameIns *wechat
func InitWechat(conf WechatConfig) {
WechatIns = NewWechatIns(&conf, Constants.AuthChannel.Wechat)
}
func InitWechatGame(conf WechatConfig) {
WechatGameIns = NewWechatIns(&conf, Constants.AuthChannel.WechatGame)
}
func NewWechatIns(conf *WechatConfig, channel int) *wechat {
newWechat := &wechat{
config: conf,
channel: channel,
}
return newWechat
}
const ()
type wechat struct {
config *WechatConfig
channel int
}
// CommonError 微信返回的通用错误json
type CommonError struct {
ErrCode int64 `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
// ResAccessToken 获取用户授权access_token的返回结果
type resAccessToken struct {
CommonError
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
OpenID string `json:"openid"`
Scope string `json:"scope"`
}
// Config 返回给用户jssdk配置信息
type JsConfig struct {
AppID string `json:"app_id"`
Timestamp int64 `json:"timestamp"`
NonceStr string `json:"nonce_str"`
Signature string `json:"signature"`
}
// resTicket 请求jsapi_tikcet返回结果
type resTicket struct {
CommonError
Ticket string `json:"ticket"`
ExpiresIn int64 `json:"expires_in"`
}
func (w *wechat) GetJsConfig(uri string) (config *JsConfig, err error) {
config = new(JsConfig)
var ticketStr string
ticketStr, err = w.getTicket()
if err != nil {
return
}
nonceStr := randomStr(16)
timestamp := UnixtimeSec()
str := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s", ticketStr, nonceStr, timestamp, uri)
sigStr := signature(str)
config.AppID = w.config.AppID
config.NonceStr = nonceStr
config.Timestamp = timestamp
config.Signature = sigStr
return
}
// TODO add cache, getTicket 获取jsapi_ticket
func (w *wechat) getTicket() (ticketStr string, err error) {
var ticket resTicket
ticket, err = w.getTicketFromServer()
if err != nil {
return
}
ticketStr = ticket.Ticket
return
}
func (w *wechat) GetOauth2Basic(authcode string) (result resAccessToken, err error) {
var accessTokenURL string
if w.channel == Constants.AuthChannel.WechatGame {
accessTokenURL = w.config.ApiProtocol + "://" + w.config.ApiGateway + "/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
}
if w.channel == Constants.AuthChannel.Wechat {
accessTokenURL = w.config.ApiProtocol + "://" + w.config.ApiGateway + "/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
}
urlStr := fmt.Sprintf(accessTokenURL, w.config.AppID, w.config.AppSecret, authcode)
Logger.Info("urlStr", zap.Any(":", urlStr))
response, err := get(urlStr)
if err != nil {
return
}
err = json.Unmarshal(response, &result)
if err != nil {
Logger.Info("GetOauth2Basic marshal error", zap.Any("err", err))
return
}
if result.ErrCode != 0 {
err = fmt.Errorf("GetOauth2Basic error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
return
}
return
}
// UserInfo 用户授权获取到用户信息
type UserInfo struct {
CommonError
OpenID string `json:"openid"`
Nickname string `json:"nickname"`
Sex int32 `json:"sex"`
Province string `json:"province"`
City string `json:"city"`
Country string `json:"country"`
HeadImgURL string `json:"headimgurl"`
Privilege []string `json:"privilege"`
Unionid string `json:"unionid"`
}
func (w *wechat) GetMoreInfo(accessToken string, openId string) (result UserInfo, err error) {
userInfoURL := w.config.ApiProtocol + "://" + w.config.ApiGateway + "/sns/userinfo?appid=%s&access_token=%s&openid=%s&lang=zh_CN"
urlStr := fmt.Sprintf(userInfoURL, w.config.AppID, accessToken, openId)
response, err := get(urlStr)
if err != nil {
return
}
err = json.Unmarshal(response, &result)
if err != nil {
Logger.Info("GetMoreInfo marshal error", zap.Any("err", err))
return
}
if result.ErrCode != 0 {
err = fmt.Errorf("GetMoreInfo error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
return
}
return
}
// HTTPGet get 请求
func get(uri string) ([]byte, error) {
response, err := http.Get(uri)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
return body, err
}
// PostJSON post json 数据请求
func post(uri string, obj interface{}) ([]byte, error) {
jsonData, err := json.Marshal(obj)
if err != nil {
return nil, err
}
jsonData = bytes.Replace(jsonData, []byte("\\u003c"), []byte("<"), -1)
jsonData = bytes.Replace(jsonData, []byte("\\u003e"), []byte(">"), -1)
jsonData = bytes.Replace(jsonData, []byte("\\u0026"), []byte("&"), -1)
body := bytes.NewBuffer(jsonData)
response, err := http.Post(uri, "application/json;charset=utf-8", body)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
}
return ioutil.ReadAll(response.Body)
}
// Signature sha1签名
func signature(params ...string) string {
sort.Strings(params)
h := sha1.New()
for _, s := range params {
io.WriteString(h, s)
}
return fmt.Sprintf("%x", h.Sum(nil))
}
// RandomStr 随机生成字符串
func randomStr(length int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < length; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}
// getTicketFromServer 强制从服务器中获取ticket
func (w *wechat) getTicketFromServer() (ticket resTicket, err error) {
var accessToken string
accessToken, err = w.getAccessTokenFromServer()
if err != nil {
return
}
getTicketURL := w.config.ApiProtocol + "://" + w.config.ApiGateway + "/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"
var response []byte
url := fmt.Sprintf(getTicketURL, accessToken)
response, err = get(url)
err = json.Unmarshal(response, &ticket)
if err != nil {
return
}
if ticket.ErrCode != 0 {
err = fmt.Errorf("getTicket Error : errcode=%d , errmsg=%s", ticket.ErrCode, ticket.ErrMsg)
return
}
return
}
// GetAccessTokenFromServer 强制从微信服务器获取token
func (w *wechat) getAccessTokenFromServer() (accessToken string, err error) {
AccessTokenURL := w.config.ApiProtocol + "://" + w.config.ApiGateway + "/cgi-bin/token"
url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, w.config.AppID, w.config.AppSecret)
var body []byte
body, err = get(url)
if err != nil {
return
}
var r resAccessToken
err = json.Unmarshal(body, &r)
if err != nil {
return
}
if r.ErrMsg != "" {
err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", r.ErrCode, r.ErrMsg)
return
}
accessToken = r.AccessToken
return
}