This commit is contained in:
邹宗楠
2022-08-15 16:24:41 +08:00
parent 949dd96a0a
commit be87623211
15 changed files with 321 additions and 103 deletions

View File

@@ -1,9 +1,6 @@
package app_server
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -17,7 +14,6 @@ import (
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
@@ -27,14 +23,14 @@ type UserLogin struct {
}
// WxLogin 授权登录
func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberParam) (*model.User, error) {
func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WxLoginReq) (*model.User, error) {
openObj, err := api.WeixinMiniAPI.SNSCode2Session(param.Code)
if err != nil {
return nil, err
}
// 检查用户是否存在
users, err := dao.GetUsers(globals.GetDB(), "", "", "", openObj.OpenID)
users, err := dao.GetUsers(globals.GetDB(), "", "", param.Phone, openObj.OpenID)
if err != nil {
return nil, err
}
@@ -42,11 +38,6 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberPa
return nil, errors.New("数据异常,用户电话不唯一,联系管理员")
}
weChatLogin := new(wxLogin.UserPhone)
if err := DecryptOpenDataToStruct(param.EncryptedData, param.IV, openObj.SessionKey, weChatLogin); err != nil {
return nil, err
}
var userObj *model.User
timeNow := time.Now()
switch len(users) {
@@ -60,7 +51,7 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberPa
UserID: utils.GetUUID(),
Password: "",
Name: param.NickName,
Mobile: weChatLogin.PhoneNumber,
Mobile: param.Phone,
Email: "",
Avatar: param.HeadUrl,
Status: 1,
@@ -88,79 +79,106 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberPa
users[0].Avatar = param.HeadUrl
users[0].OpenId = openObj.OpenID
users[0].UnionId = openObj.UnionID
// 用户存在,判断用户
users[0].UpdatedAt = &timeNow
users[0].LastOperator = users[0].Name
users[0].LastLoginIP = ctx.ClientIP()
if err := dao.UpdateUserWx(users[0], []string{"updated_at", "last_operator", "last_login_ip", "name", "avatar", "open_id", "union_id"}); err != nil {
return nil, err
}
} else {
// 用户存在,判断用户
users[0].UpdatedAt = &timeNow
users[0].LastOperator = users[0].Name
users[0].LastLoginIP = ctx.ClientIP()
if err := dao.UpdateUserWx(users[0], []string{"updated_at", "last_operator", "last_login_ip"}); err != nil {
return nil, err
}
}
// 用户存在,判断用户
users[0].UpdatedAt = &timeNow
users[0].LastOperator = users[0].Name
users[0].LastLoginIP = ctx.ClientIP()
if err := dao.UpdateUserWx(users[0], []string{"updated_at", "last_operator", "last_login_ip", "name", "avatar", "open_id", "union_id"}); err != nil {
return nil, err
}
userObj = users[0]
}
return userObj, err
}
// DecryptOpenDataToStruct 解密开放数据到结构体
// encryptedData包括敏感数据在内的完整用户信息的加密数据小程序获取到
// iv加密算法的初始向量小程序获取到
// sessionKey会话密钥通过 gopay.Code2Session() 方法获取到
// beanPtr需要解析到的结构体指针操作完后声明的结构体会被赋值
// 文档https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
func DecryptOpenDataToStruct(encryptedData, iv, sessionKey string, beanPtr interface{}) (err error) {
if encryptedData == "" || iv == "" || sessionKey == "" {
return errors.New("input params can not null")
// MobileLogin 短信验证登录
func (u *UserLogin) MobileLogin(ctx *gin.Context, param *wxLogin.MobileLogin) (*model.User, error) {
// 校验用户信息
isHave, err := SendVerifyCodeServer.VerifySecret(param.Phone, param.BizId, param.Code)
if err != nil {
return nil, err
}
var (
cipherText, aesKey, ivKey, plainText []byte
block cipher.Block
blockMode cipher.BlockMode
)
beanValue := reflect.ValueOf(beanPtr)
if beanValue.Kind() != reflect.Ptr {
return errors.New("传入beanPtr类型必须是以指针形式")
if !isHave {
return nil, errors.New("验证码错误")
}
if beanValue.Elem().Kind() != reflect.Struct {
return errors.New("传入interface{}必须是结构体")
// 检查用户是否存在
users, err := dao.GetUsers(globals.GetDB(), "", "", param.Phone, "")
if err != nil {
return nil, err
}
cipherText, _ = base64.StdEncoding.DecodeString(encryptedData)
aesKey, _ = base64.StdEncoding.DecodeString(sessionKey)
ivKey, _ = base64.StdEncoding.DecodeString(iv)
if len(cipherText)%len(aesKey) != 0 {
return errors.New("encryptedData is error")
}
if block, err = aes.NewCipher(aesKey); err != nil {
return fmt.Errorf("aes.NewCipher%w", err)
}
blockMode = cipher.NewCBCDecrypter(block, ivKey)
plainText = make([]byte, len(cipherText))
blockMode.CryptBlocks(plainText, cipherText)
if len(plainText) > 0 {
plainText = PKCS7UnPadding(plainText)
}
if err = json.Unmarshal(plainText, beanPtr); err != nil {
return fmt.Errorf("json.Marshal(%s)%w", string(plainText), err)
}
return
}
// PKCS7UnPadding 解密填充模式(去除补全码) PKCS7UnPadding
// 解密时需要在最后面去掉加密时添加的填充byte
func PKCS7UnPadding(origData []byte) (bs []byte) {
length := len(origData)
unPaddingNumber := int(origData[length-1]) // 找到Byte数组最后的填充byte 数字
if unPaddingNumber <= 16 {
bs = origData[:(length - unPaddingNumber)] // 只截取返回有效数字内的byte数组
} else {
bs = origData
var userObj *model.User
timeNow := time.Now()
switch len(users) {
case 1: // 用户存在
userObj = users[0]
case 0: // 用户不存在
// 用户不存在,创建并返回用户
userBase := &model.User{
CreatedAt: &timeNow,
UpdatedAt: &timeNow,
LastOperator: "系统新增",
DeletedAt: &utils.DefaultTimeValue,
UserID: utils.GetUUID(),
Password: "",
Name: "手机用户_" + param.Phone,
Mobile: param.Phone,
Email: "",
Avatar: "",
Status: 1,
Type: 1,
Company: "",
CityCode: 0,
DistrictCode: 0,
Address: "",
IDCardNo: "",
Remark: "",
LastLoginAt: &timeNow,
LastLoginIP: ctx.ClientIP(),
LastLoginType: model.OrderOriginMobile,
OpenId: "",
UnionId: "",
}
if err := dao.CreateUserWx(userBase); err != nil {
return nil, err
}
userObj = userBase
}
return
return userObj, nil
}
// GetUserPhoneNum 解密用户手机号
func (u *UserLogin) GetUserPhoneNum(param *wxLogin.WeChatPhoneNumberParam) (string, error) {
return api.WeixinMiniAPI.SNSGetUserPhone(param.Code)
func (u *UserLogin) GetUserPhoneNum(param *wxLogin.WeChatPhoneNumberParam) (string, bool, error) {
phone, err := api.WeixinMiniAPI.SNSGetUserPhone(param.Code)
if err != nil {
return "", false, err
}
// 检查用户是否存在
users, err := dao.GetUsers(globals.GetDB(), "", "", phone, "")
if err != nil {
return "", false, err
}
if len(users) != 1 {
return phone, false, nil
}
isRegion := false
if users[0].OpenId != "" {
isRegion = true
}
return phone, isRegion, nil
}
// Jxc4UserToken 全局变量,缓存菜市管理系统token