feat: openid login

This commit is contained in:
zzw
2026-09-02 17:37:12 +08:00
parent 540f74f9b7
commit bb14f9e324
38 changed files with 1627 additions and 1091 deletions
+13
View File
@@ -0,0 +1,13 @@
package auth
type Client struct {
AppID string
Secret string
}
func NewClient(appID, secret string) *Client {
return &Client{
AppID: appID,
Secret: secret,
}
}
+78
View File
@@ -0,0 +1,78 @@
package auth
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"lone-services/pkg/utils"
)
const phoneNumberURL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
type PhoneNumberRequest struct {
Code string `json:"code"`
}
type PhoneInfo struct {
PhoneNumber string `json:"phoneNumber"`
PurePhoneNumber string `json:"purePhoneNumber"`
CountryCode string `json:"countryCode"`
}
type PhoneNumberResult struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
PhoneInfo PhoneInfo `json:"phone_info"`
}
type APIError struct {
ErrCode int
ErrMsg string
}
func (e *APIError) Error() string {
return fmt.Sprintf("errcode=%d errmsg=%s", e.ErrCode, e.ErrMsg)
}
func (c *Client) GetPhoneNumber(accessToken, code string) (*PhoneInfo, error) {
if accessToken == utils.StringEmpty {
return nil, fmt.Errorf("wechat access_token empty")
}
if code == utils.StringEmpty {
return nil, fmt.Errorf("wechat phone code empty")
}
payload, err := json.Marshal(PhoneNumberRequest{Code: code})
if err != nil {
return nil, err
}
url := fmt.Sprintf("%s?access_token=%s", phoneNumberURL, accessToken)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Post(url, "application/json", bytes.NewReader(payload))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result PhoneNumberResult
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
if result.ErrCode != utils.NumberZero {
return nil, &APIError{ErrCode: result.ErrCode, ErrMsg: result.ErrMsg}
}
if result.PhoneInfo.PhoneNumber == utils.StringEmpty && result.PhoneInfo.PurePhoneNumber == utils.StringEmpty {
return nil, fmt.Errorf("wechat phone number empty")
}
return &result.PhoneInfo, nil
}
+50
View File
@@ -0,0 +1,50 @@
package auth
import (
"fmt"
"lone-services/pkg/utils"
jsoniter "github.com/json-iterator/go"
)
const code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session"
type Session struct {
OpenID string `json:"openid"`
UnionID string `json:"unionid"`
SessionKey string `json:"session_key"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
func (c *Client) Code2Session(code string) (*Session, error) {
if c == nil || c.AppID == utils.StringEmpty || c.Secret == utils.StringEmpty {
return nil, fmt.Errorf("wechat app config missing")
}
if code == utils.StringEmpty {
return nil, fmt.Errorf("wechat code empty")
}
body, err := utils.Curl(code2SessionURL).SetData(map[string]string{
"appid": c.AppID,
"secret": c.Secret,
"js_code": code,
"grant_type": "authorization_code",
}).Get("")
if err != nil {
return nil, err
}
var session Session
if err := jsoniter.UnmarshalFromString(body, &session); err != nil {
return nil, err
}
if session.ErrCode != utils.NumberZero {
return nil, fmt.Errorf("wechat code2session: %d %s", session.ErrCode, session.ErrMsg)
}
if session.OpenID == utils.StringEmpty {
return nil, fmt.Errorf("wechat openid empty")
}
return &session, nil
}
+45
View File
@@ -0,0 +1,45 @@
package auth
import (
"fmt"
"lone-services/pkg/utils"
jsoniter "github.com/json-iterator/go"
)
const accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
type AccessTokenResult struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
}
func (c *Client) AccessToken() (*AccessTokenResult, error) {
if c == nil || c.AppID == utils.StringEmpty || c.Secret == utils.StringEmpty {
return nil, fmt.Errorf("wechat app config missing")
}
body, err := utils.Curl(accessTokenURL).SetData(map[string]string{
"grant_type": "client_credential",
"appid": c.AppID,
"secret": c.Secret,
}).Get("")
if err != nil {
return nil, err
}
var result AccessTokenResult
if err := jsoniter.UnmarshalFromString(body, &result); err != nil {
return nil, err
}
if result.ErrCode != utils.NumberZero {
return nil, fmt.Errorf("wechat access_token: %d %s", result.ErrCode, result.ErrMsg)
}
if result.AccessToken == utils.StringEmpty {
return nil, fmt.Errorf("wechat access_token empty")
}
return &result, nil
}