1
This commit is contained in:
@@ -16,7 +16,7 @@ var Auth2ControllerController = new(Auth2Controller)
|
|||||||
// Login 登录接口
|
// Login 登录接口
|
||||||
// @Title 登录接口
|
// @Title 登录接口
|
||||||
// @Description 登录接口(微信与公众号登录不能直接调用此接口)
|
// @Description 登录接口(微信与公众号登录不能直接调用此接口)
|
||||||
// @Param data body app_model.WxLoginReq true "请求参数"
|
// @Param data body app_model.WeChatPhoneNumberParam true "请求参数"
|
||||||
// @Success 200 {object} controllers.CallResult
|
// @Success 200 {object} controllers.CallResult
|
||||||
// @Failure 200 {object} controllers.CallResult
|
// @Failure 200 {object} controllers.CallResult
|
||||||
// @router /Login [post]
|
// @router /Login [post]
|
||||||
@@ -24,7 +24,7 @@ func (a *Auth2Controller) Login(c *gin.Context) {
|
|||||||
// 参数绑定
|
// 参数绑定
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
params *app_model.WxLoginReq
|
params *app_model.WeChatPhoneNumberParam
|
||||||
service = app_server.UserLogin{}
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ const (
|
|||||||
|
|
||||||
// WeChatPhoneNumberParam 微信登陆
|
// WeChatPhoneNumberParam 微信登陆
|
||||||
type WeChatPhoneNumberParam struct {
|
type WeChatPhoneNumberParam struct {
|
||||||
|
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
|
Code string `json:"code" form:"code" binding:"required"` //code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package app_server
|
package app_server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.rosy.net.cn/baseapi/utils"
|
"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/dao"
|
||||||
"git.rosy.net.cn/jx-print/globals"
|
"git.rosy.net.cn/jx-print/globals"
|
||||||
"git.rosy.net.cn/jx-print/model"
|
"git.rosy.net.cn/jx-print/model"
|
||||||
@@ -14,6 +18,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,15 +28,14 @@ type UserLogin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WxLogin 授权登录
|
// WxLogin 授权登录
|
||||||
func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User, error) {
|
func (u *UserLogin) WxLogin(ctx *gin.Context, param *wxLogin.WeChatPhoneNumberParam) (*model.User, error) {
|
||||||
// 生成openId
|
openObj, err := api.WeixinMiniAPI.SNSCode2Session(param.Code)
|
||||||
openObj, err := api.WeixinMiniAPI.SNSRetrieveToken(code)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查用户是否存在
|
// 检查用户是否存在
|
||||||
users, err := dao.GetUsers(globals.GetDB(), "", "", phone, "")
|
users, err := dao.GetUsers(globals.GetDB(), "", "", "", openObj.OpenID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -39,9 +43,8 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User,
|
|||||||
return nil, errors.New("数据异常,用户电话不唯一,联系管理员")
|
return nil, errors.New("数据异常,用户电话不唯一,联系管理员")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户信息,是否注册
|
weChatLogin := new(wechat.UserPhone)
|
||||||
userInfo, err := api.WeixinMiniAPI.SNSGetUserInfo(openObj.AccessToken, openObj.OpenID)
|
if err := DecryptOpenDataToStruct(param.EncryptedData, param.IV, openObj.SessionKey, weChatLogin); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +60,10 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User,
|
|||||||
DeletedAt: &utils.DefaultTimeValue,
|
DeletedAt: &utils.DefaultTimeValue,
|
||||||
UserID: utils.GetUUID(),
|
UserID: utils.GetUUID(),
|
||||||
Password: "",
|
Password: "",
|
||||||
Name: userInfo.NickName,
|
Name: param.NickName,
|
||||||
Mobile: phone,
|
Mobile: weChatLogin.PhoneNumber,
|
||||||
Email: "",
|
Email: "",
|
||||||
Avatar: userInfo.HeadImgURL,
|
Avatar: param.HeadUrl,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
Type: 1,
|
Type: 1,
|
||||||
Company: "",
|
Company: "",
|
||||||
@@ -72,8 +75,8 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User,
|
|||||||
LastLoginAt: &timeNow,
|
LastLoginAt: &timeNow,
|
||||||
LastLoginIP: ctx.ClientIP(),
|
LastLoginIP: ctx.ClientIP(),
|
||||||
LastLoginType: model.OrderOriginWxMini,
|
LastLoginType: model.OrderOriginWxMini,
|
||||||
OpenId: userInfo.OpenID,
|
OpenId: openObj.OpenID,
|
||||||
UnionId: userInfo.UnionID,
|
UnionId: openObj.UnionID,
|
||||||
}
|
}
|
||||||
if err := dao.CreateUserWx(userBase); err != nil {
|
if err := dao.CreateUserWx(userBase); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -82,10 +85,10 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User,
|
|||||||
case 1:
|
case 1:
|
||||||
// 用户使用电话号码登录,未使用微信登录
|
// 用户使用电话号码登录,未使用微信登录
|
||||||
if users[0].OpenId == "" {
|
if users[0].OpenId == "" {
|
||||||
users[0].Name = userInfo.NickName
|
users[0].Name = param.NickName
|
||||||
users[0].Avatar = userInfo.HeadImgURL
|
users[0].Avatar = param.HeadUrl
|
||||||
users[0].OpenId = userInfo.OpenID
|
users[0].OpenId = openObj.OpenID
|
||||||
users[0].UnionId = userInfo.UnionID
|
users[0].UnionId = openObj.UnionID
|
||||||
}
|
}
|
||||||
// 用户存在,判断用户
|
// 用户存在,判断用户
|
||||||
users[0].UpdatedAt = &timeNow
|
users[0].UpdatedAt = &timeNow
|
||||||
@@ -100,6 +103,62 @@ func (u *UserLogin) WxLogin(ctx *gin.Context, code, phone string) (*model.User,
|
|||||||
return userObj, err
|
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 解密用户手机号
|
// GetUserPhoneNum 解密用户手机号
|
||||||
func (u *UserLogin) GetUserPhoneNum(param *wxLogin.WeChatPhoneNumberParam) (string, error) {
|
func (u *UserLogin) GetUserPhoneNum(param *wxLogin.WeChatPhoneNumberParam) (string, error) {
|
||||||
return api.WeixinMiniAPI.SNSGetUserPhone(param.Code)
|
return api.WeixinMiniAPI.SNSGetUserPhone(param.Code)
|
||||||
|
|||||||
Reference in New Issue
Block a user