You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
2.6 KiB
100 lines
2.6 KiB
package wechat_auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
)
|
|
|
|
type (
|
|
ISys interface {
|
|
Auth(ctx context.Context, idToken string) (info *WeChatUserInfoResponse, err error)
|
|
}
|
|
WeChatTokenResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
OpenID string `json:"openid"`
|
|
Scope string `json:"scope"`
|
|
WeChatError // 内嵌错误结构
|
|
}
|
|
|
|
WeChatUserInfoResponse struct {
|
|
OpenID string `json:"openid"`
|
|
Nickname string `json:"nickname"`
|
|
HeadImgURL string `json:"headimgurl"`
|
|
UnionID string `json:"unionid"`
|
|
WeChatError
|
|
}
|
|
|
|
WeChatError struct {
|
|
ErrCode int `json:"errcode"`
|
|
ErrMsg string `json:"errmsg"`
|
|
}
|
|
)
|
|
|
|
// defsys 由 OnInit(启动) 与 Reload(后台改配置后热更) 写、Auth 读,故必须加锁:
|
|
// 无锁并发读写接口值是 data race。
|
|
var (
|
|
mu sync.RWMutex
|
|
defsys ISys
|
|
)
|
|
|
|
func get() ISys {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return defsys
|
|
}
|
|
|
|
func set(sys ISys) {
|
|
mu.Lock()
|
|
defsys = sys
|
|
mu.Unlock()
|
|
}
|
|
|
|
// ErrNotInited 微信登录未初始化。OnInit 失败时调用方可选择降级;此时包级函数返回本错误,而不是 panic。
|
|
var ErrNotInited = errors.New("wechat_auth 未初始化:配置缺失")
|
|
|
|
// OnInit 配置缺失时返回 error(调用方降级为「微信登录不可用」),不再像以前那样空着 AppSecret
|
|
// 也报 success——那会让启动日志说一切正常、实际每次登录都被微信回 41004 appsecret missing。
|
|
func OnInit(config map[string]interface{}, option ...Option) error {
|
|
options, err := newOptionsChecked(config, option...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sys, err := newSys(options)
|
|
if err != nil {
|
|
// 出错时不要给 defsys 赋值,避免「非 nil 接口 + nil 指针」让守卫失效。
|
|
return err
|
|
}
|
|
set(sys)
|
|
return nil
|
|
}
|
|
|
|
// Reload 后台「业务功能配置 → 微信登录」保存后热更,无需重启。
|
|
// 校验不过直接返回 error、保留旧实例继续服务(与 sys/sms 的 Reload 同口径)。
|
|
func Reload(config map[string]interface{}, option ...Option) error {
|
|
options, err := newOptionsChecked(config, option...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sys, err := newSys(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
set(sys)
|
|
return nil
|
|
}
|
|
|
|
func NewSys(option ...Option) (sys ISys, err error) {
|
|
sys, err = newSys(newOptionsByOption(option...))
|
|
return
|
|
}
|
|
|
|
func Auth(ctx context.Context, idToken string) (info *WeChatUserInfoResponse, err error) {
|
|
sys := get()
|
|
if sys == nil {
|
|
return nil, ErrNotInited
|
|
}
|
|
return sys.Auth(ctx, idToken)
|
|
}
|
|
|