From 6fa1bc127dfba7c8cf64caad5322bda368c7407b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Fri, 12 Aug 2022 11:22:22 +0800 Subject: [PATCH] 1 --- controllers/app/wx_login.go | 6 +- model/app_model/app_user_login.go | 6 +- services/print_server/app_server/wx_login.go | 91 ++++++++++++++++---- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/controllers/app/wx_login.go b/controllers/app/wx_login.go index 45a8d62..a6d299e 100644 --- a/controllers/app/wx_login.go +++ b/controllers/app/wx_login.go @@ -16,7 +16,7 @@ var Auth2ControllerController = new(Auth2Controller) // Login 登录接口 // @Title 登录接口 // @Description 登录接口(微信与公众号登录不能直接调用此接口) -// @Param data body app_model.WxLoginReq true "请求参数" +// @Param data body app_model.WeChatPhoneNumberParam true "请求参数" // @Success 200 {object} controllers.CallResult // @Failure 200 {object} controllers.CallResult // @router /Login [post] @@ -24,7 +24,7 @@ func (a *Auth2Controller) Login(c *gin.Context) { // 参数绑定 var ( err error - params *app_model.WxLoginReq + params *app_model.WeChatPhoneNumberParam service = app_server.UserLogin{} ) @@ -37,7 +37,7 @@ func (a *Auth2Controller) Login(c *gin.Context) { } controllers.CallFunc(c, func() (retVal interface{}, errCode string, err error) { - user, err := service.WxLogin(c, params.Code, params.Phone) + user, err := service.WxLogin(c, params) if err != nil { return nil, "", err } diff --git a/model/app_model/app_user_login.go b/model/app_model/app_user_login.go index 5843028..3fd1d7e 100644 --- a/model/app_model/app_user_login.go +++ b/model/app_model/app_user_login.go @@ -21,7 +21,11 @@ const ( // WeChatPhoneNumberParam 微信登陆 type WeChatPhoneNumberParam struct { - Code string `json:"code" form:"code" binding:"required"` //code + EncryptedData string `json:"encrypted_data" form:"encrypted_data" binding:"required"` // 加密信息 + IV string `json:"iv" form:"iv" binding:"required"` // 加密算法初始量 + NickName string `json:"nick_name" form:"nick_name" binding:"required"` // 昵称 + HeadUrl string `json:"head_url" form:"head_url" binding:"required"` //头像图片地址 + Code string `json:"code" form:"code" binding:"required"` //code } type WxLoginReq struct { diff --git a/services/print_server/app_server/wx_login.go b/services/print_server/app_server/wx_login.go index 743d3d5..4e7ede7 100644 --- a/services/print_server/app_server/wx_login.go +++ b/services/print_server/app_server/wx_login.go @@ -1,10 +1,14 @@ package app_server import ( + "crypto/aes" + "crypto/cipher" + "encoding/base64" "encoding/json" "errors" "fmt" "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/gopay-main/wechat" "git.rosy.net.cn/jx-print/dao" "git.rosy.net.cn/jx-print/globals" "git.rosy.net.cn/jx-print/model" @@ -14,6 +18,7 @@ import ( "io/ioutil" "net/http" "net/url" + "reflect" "strconv" "strings" "time" @@ -23,15 +28,14 @@ type UserLogin struct { } // WxLogin 授权登录 -func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, error) { - // 生成openId - openObj, err := api.WeixinMiniAPI.SNSRetrieveToken(code) +func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberParam) (*model.User, error) { + openObj, err := api.WeixinMiniAPI.SNSCode2Session(param.Code) if err != nil { return nil, err } // 检查用户是否存在 - users, err := dao.GetUsers(globals.GetDB(), "", "", phone, "") + users, err := dao.GetUsers(globals.GetDB(), "", "", "", openObj.OpenID) if err != nil { return nil, err } @@ -39,9 +43,8 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, return nil, errors.New("数据异常,用户电话不唯一,联系管理员") } - // 获取用户信息,是否注册 - userInfo, err := api.WeixinMiniAPI.SNSGetUserInfo(openObj.AccessToken, openObj.OpenID) - if err != nil { + weChatLogin := new(wechat.UserPhone) + if err := DecryptOpenDataToStruct(param.EncryptedData, param.IV, openObj.SessionKey, weChatLogin); err != nil { return nil, err } @@ -57,10 +60,10 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, DeletedAt: &utils.DefaultTimeValue, UserID: utils.GetUUID(), Password: "", - Name: userInfo.NickName, - Mobile: phone, + Name: param.NickName, + Mobile: weChatLogin.PhoneNumber, Email: "", - Avatar: userInfo.HeadImgURL, + Avatar: param.HeadUrl, Status: 1, Type: 1, Company: "", @@ -72,8 +75,8 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, LastLoginAt: &timeNow, LastLoginIP: ctx.ClientIP(), LastLoginType: model.OrderOriginWxMini, - OpenId: userInfo.OpenID, - UnionId: userInfo.UnionID, + OpenId: openObj.OpenID, + UnionId: openObj.UnionID, } if err := dao.CreateUserWx(userBase); err != nil { return nil, err @@ -82,10 +85,10 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, case 1: // 用户使用电话号码登录,未使用微信登录 if users[0].OpenId == "" { - users[0].Name = userInfo.NickName - users[0].Avatar = userInfo.HeadImgURL - users[0].OpenId = userInfo.OpenID - users[0].UnionId = userInfo.UnionID + users[0].Name = param.NickName + users[0].Avatar = param.HeadUrl + users[0].OpenId = openObj.OpenID + users[0].UnionId = openObj.UnionID } // 用户存在,判断用户 users[0].UpdatedAt = &timeNow @@ -100,6 +103,62 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, 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") + } + 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 beanValue.Elem().Kind() != reflect.Struct { + return errors.New("传入interface{}必须是结构体") + } + 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 + } + return +} + // GetUserPhoneNum 解密用户手机号 func (u *UserLogin) GetUserPhoneNum(param *wxLogin.WeChatPhoneNumberParam) (string, error) { return api.WeixinMiniAPI.SNSGetUserPhone(param.Code)