Files
hm-server-v2/app/main/api/internal/logic/user/getsignaturelogic.go
2025-10-15 00:01:02 +08:00

186 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package user
import (
"context"
"crypto/sha1"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strings"
"time"
"hm-server/app/main/api/internal/svc"
"hm-server/app/main/api/internal/types"
"hm-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type GetSignatureLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetSignatureLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSignatureLogic {
return &GetSignatureLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetSignatureLogic) GetSignature(req *types.GetSignatureReq) (resp *types.GetSignatureResp, err error) {
// 1. 获取access_token
accessToken, err := l.getAccessToken()
if err != nil {
l.Errorf("获取access_token失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取access_token失败: %v", err)
}
// 2. 获取jsapi_ticket
jsapiTicket, err := l.getJsapiTicket(accessToken)
if err != nil {
l.Errorf("获取jsapi_ticket失败: %v", err)
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取jsapi_ticket失败: %v", err)
}
// 3. 生成签名
timestamp := time.Now().Unix()
nonceStr := l.generateNonceStr(16)
signature := l.generateSignature(jsapiTicket, nonceStr, timestamp, req.Url)
// 4. 返回完整的JS-SDK配置信息
return &types.GetSignatureResp{
AppId: l.svcCtx.Config.WechatH5.AppID,
Timestamp: timestamp,
NonceStr: nonceStr,
Signature: signature,
}, nil
}
// getAccessToken 获取微信公众号access_token
func (l *GetSignatureLogic) getAccessToken() (string, error) {
appID := l.svcCtx.Config.WechatH5.AppID
appSecret := l.svcCtx.Config.WechatH5.AppSecret
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appID, appSecret)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取access_token失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.AccessToken, nil
}
// getJsapiTicket 获取jsapi_ticket
func (l *GetSignatureLogic) getJsapiTicket(accessToken string) (string, error) {
url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var result struct {
Ticket string `json:"ticket"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
if err = json.Unmarshal(body, &result); err != nil {
return "", err
}
if result.ErrCode != 0 {
return "", fmt.Errorf("获取jsapi_ticket失败: errcode=%d, errmsg=%s", result.ErrCode, result.ErrMsg)
}
return result.Ticket, nil
}
// generateNonceStr 生成随机字符串
func (l *GetSignatureLogic) generateNonceStr(length int) string {
chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
result := make([]byte, length)
for i := 0; i < length; i++ {
result[i] = chars[i%len(chars)]
}
return string(result)
}
// generateSignature 生成签名
func (l *GetSignatureLogic) generateSignature(jsapiTicket, nonceStr string, timestamp int64, urlStr string) string {
// 对URL进行解码避免重复编码
decodedURL, err := url.QueryUnescape(urlStr)
if err != nil {
decodedURL = urlStr
}
// 构建签名字符串
params := map[string]string{
"jsapi_ticket": jsapiTicket,
"noncestr": nonceStr,
"timestamp": fmt.Sprintf("%d", timestamp),
"url": decodedURL,
}
// 对参数进行字典序排序
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// 拼接字符串
var signStr strings.Builder
for i, k := range keys {
if i > 0 {
signStr.WriteString("&")
}
signStr.WriteString(k)
signStr.WriteString("=")
signStr.WriteString(params[k])
}
// SHA1加密
h := sha1.New()
h.Write([]byte(signStr.String()))
signature := fmt.Sprintf("%x", h.Sum(nil))
return signature
}