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 }