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