commit
df52328b0b
@ -0,0 +1 @@
|
||||
.idea
|
@ -0,0 +1,44 @@
|
||||
# 微信登录工具
|
||||
|
||||
### 公众号
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.oa00.com/go/wx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化配置
|
||||
wx.InitWxopen("appid", "secret")
|
||||
|
||||
// 获取accessToken和openId
|
||||
wx.Wxopen.GetAccessInfo("code")
|
||||
|
||||
// 获取用户信息
|
||||
wx.Wxopen.GetUserInfo("accessToken", "openId")
|
||||
}
|
||||
```
|
||||
|
||||
### 小程序
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.oa00.com/go/wx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化配置
|
||||
wx.InitWxapp("appid","secret")
|
||||
|
||||
// 获取登录信息
|
||||
wx.Wxapp.GetLoginInfo("code")
|
||||
|
||||
// 获取用户信息
|
||||
wx.Wxapp.GetUserInfo("sessionKey", "encryptedData", "iv")
|
||||
|
||||
// 获取手机号
|
||||
wx.Wxapp.GetPhone("sessionKey", "encryptedData", "iv")
|
||||
}
|
||||
```
|
@ -0,0 +1,80 @@
|
||||
package wx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var Wxopen = &wxopen{}
|
||||
|
||||
type wxopen struct {
|
||||
appid string
|
||||
secret string
|
||||
}
|
||||
|
||||
// InitWxopen @Title 初始化开放平台
|
||||
func InitWxopen(appid, secret string) {
|
||||
Wxopen.appid = appid
|
||||
Wxopen.secret = secret
|
||||
}
|
||||
|
||||
type AccessInfo struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Openid string `json:"openid"`
|
||||
Scope string `json:"scope"`
|
||||
Unionid string `json:"unionid"`
|
||||
}
|
||||
type errInfo struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
type resAccess struct {
|
||||
errInfo
|
||||
AccessInfo
|
||||
}
|
||||
|
||||
// GetAccessInfo @Title 获取微信登录验证结构体
|
||||
func (w *wxopen) GetAccessInfo(code string) (result AccessInfo, err error) {
|
||||
res, err := request(get, fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%v&secret=%v&code=%v&grant_type=authorization_code", w.appid, w.secret, code), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var resAcc resAccess
|
||||
if err = json.Unmarshal(res, &resAcc); err != nil {
|
||||
return
|
||||
}
|
||||
if resAcc.Errcode != 0 {
|
||||
return result, errors.New(resAcc.Errmsg)
|
||||
}
|
||||
result = resAcc.AccessInfo
|
||||
return
|
||||
}
|
||||
|
||||
type userInfo struct {
|
||||
City string `json:"city"`
|
||||
Country string `json:"country"`
|
||||
Headimgurl string `json:"headimgurl"`
|
||||
Nickname string `json:"nickname"`
|
||||
Openid string `json:"openid"`
|
||||
Privilege []string `json:"privilege"`
|
||||
Province string `json:"province"`
|
||||
Sex uint `json:"sex"`
|
||||
Unionid string `json:"unionid"`
|
||||
}
|
||||
|
||||
// GetUserInfo @Title 获取微信登录会员信息
|
||||
func (w *wxopen) GetUserInfo(accessToken, openId string) (*userInfo, error) {
|
||||
res, err := request(get, fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN", accessToken, openId), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := userInfo{}
|
||||
if err := json.Unmarshal(res, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package wx
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
post = "POST"
|
||||
get = "GET"
|
||||
)
|
||||
|
||||
var client = &http.Client{}
|
||||
|
||||
// @Title 请求
|
||||
func request(method, url, data string, headers ...map[string]string) ([]byte, error) {
|
||||
reqest, err := http.NewRequest(method, url, strings.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(headers) > 0 {
|
||||
for key, value := range headers[0] {
|
||||
reqest.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
response, err := client.Do(reqest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
result, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package wx
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var Wxapp = &wxapp{}
|
||||
|
||||
type wxapp struct {
|
||||
appid string
|
||||
secret string
|
||||
}
|
||||
|
||||
// InitWxapp @Title 初始化wxapp
|
||||
func InitWxapp(appid, secret string) {
|
||||
Wxapp.appid = appid
|
||||
Wxapp.secret = secret
|
||||
}
|
||||
|
||||
type resLogin struct {
|
||||
LoginInfo
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
type LoginInfo struct {
|
||||
SessionKey string `json:"session_key"`
|
||||
Openid string `json:"openid"`
|
||||
Unionid string `json:"unionid"`
|
||||
}
|
||||
|
||||
// GetLoginInfo @Title 获取登录信息
|
||||
func (w *wxapp) GetLoginInfo(code string) (info LoginInfo, err error) {
|
||||
bytes, err := request("get", fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
|
||||
w.appid, w.secret, code), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Println(string(bytes))
|
||||
res := resLogin{}
|
||||
if err = json.Unmarshal(bytes, &res); err != nil {
|
||||
return
|
||||
}
|
||||
if res.Errcode > 0 {
|
||||
return info, errors.New(res.Errmsg)
|
||||
}
|
||||
return res.LoginInfo, nil
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
OpenID string `json:"openId"`
|
||||
UnionID string `json:"unionId"`
|
||||
NickName string `json:"nickName"`
|
||||
Gender int `json:"gender"`
|
||||
City string `json:"city"`
|
||||
Province string `json:"province"`
|
||||
Country string `json:"country"`
|
||||
AvatarURL string `json:"avatarUrl"`
|
||||
Language string `json:"language"`
|
||||
Watermark struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
AppID string `json:"appid"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
// GetUserInfo @Title 获取用户信息
|
||||
func (w *wxapp) GetUserInfo(sessionKey, encryptedData, iv string) (userInfo UserInfo, err error) {
|
||||
decrypt, err := w.Decrypt(sessionKey, encryptedData, iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(decrypt, &userInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if userInfo.Watermark.AppID != w.appid {
|
||||
return userInfo, errors.New("app id not match")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UserPhone struct {
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
PurePhoneNumber string `json:"purePhoneNumber"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
Watermark struct {
|
||||
Timestamp int `json:"timestamp"`
|
||||
Appid string `json:"appid"`
|
||||
} `json:"watermark"`
|
||||
}
|
||||
|
||||
// GetPhone @Title 获取用户手机号
|
||||
func (w *wxapp) GetPhone(sessionKey, encryptedData, iv string) (userPhone UserPhone, err error) {
|
||||
decrypt, err := w.Decrypt(sessionKey, encryptedData, iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(decrypt, &userPhone); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt @Title 解密数据
|
||||
func (w *wxapp) Decrypt(sessionKey, encryptedData, iv string) (decrypt []byte, err error) {
|
||||
aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ivBytes, err := base64.StdEncoding.DecodeString(iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, ivBytes)
|
||||
mode.CryptBlocks(cipherText, cipherText)
|
||||
decrypt, err = w.pkcs7Unpad(cipherText, block.BlockSize())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *wxapp) pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid block size")
|
||||
}
|
||||
if len(data)%blockSize != 0 || len(data) == 0 {
|
||||
return nil, errors.New("invalid PKCS7 data")
|
||||
}
|
||||
c := data[len(data)-1]
|
||||
n := int(c)
|
||||
if n == 0 || n > len(data) {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if data[len(data)-n+i] != c {
|
||||
return nil, errors.New("invalid padding on input")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-n], nil
|
||||
}
|
Loading…
Reference in new issue