Files
toutoukan/controllers/user/userLogin.go
2025-08-11 00:45:06 +08:00

196 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package user
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"math/rand"
"net/http"
"net/url"
"strconv"
"time"
"toutoukan/config"
"toutoukan/init/databaseInit"
"toutoukan/model/usermodel"
"toutoukan/utill"
)
const wxLoginURL = "https://api.weixin.qq.com/sns/jscode2session"
func UserLogin(c *gin.Context) {
fmt.Println("Request Body:", c.Request.Body)
var req usermodel.WxLoginM
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
fmt.Println("req:123")
fmt.Println(req.EncryptedData)
fmt.Println(req.Code)
fmt.Println(req.Iv)
fmt.Println("使用的appid:", config.Conf.WxID)
fmt.Println("使用的secret:", config.Conf.Wxsecret)
//----------------发送验证请求
params := url.Values{}
params.Add("appid", config.Conf.WxID)
params.Add("secret", config.Conf.Wxsecret)
params.Add("js_code", req.Code)
params.Add("grant_type", "authorization_code")
requestURL := fmt.Sprintf("%s?%s", wxLoginURL, params.Encode())
fmt.Println("微信接口请求URL:", requestURL)
resp, err := http.Get(fmt.Sprintf("%s?%s", wxLoginURL, params.Encode()))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "调用微信登录接口失败: " + err.Error()})
return
}
fmt.Println("微信登录返回结果:", resp.Body)
defer resp.Body.Close()
// 解析微信返回结果
var wxResp usermodel.WxLoginResponse
if err := json.NewDecoder(resp.Body).Decode(&wxResp); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解析微信登录响应失败: " + err.Error(), "code": "10026"})
return
}
// 检查微信返回的错误
if wxResp.ErrCode != 0 {
c.JSON(http.StatusBadRequest, gin.H{ // http.StatusBadRequest 对应 400
"error": fmt.Sprintf("微信登录失败: %s (错误码: %d)", wxResp.ErrCode, wxResp.ErrMsg),
"code": "10027",
})
return
}
//可选使用session_key、encryptedData和iv解密用户信息
// 步骤2解密手机号复用AES解密函数
phoneData, err := decryptWxData(wxResp.SessionKey, req.EncryptedData, req.Iv)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解密失败: " + err.Error()})
return
}
// 步骤3解析为手机号结构体
var phoneInfo usermodel.WxPhoneInfo
if err := json.Unmarshal(phoneData, &phoneInfo); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解析手机号失败"})
return
}
// 验证水印(确保数据有效性)
if phoneInfo.Watermark.AppID != config.Conf.WxID {
c.JSON(http.StatusForbidden, gin.H{"error": "数据水印验证失败"})
return
}
fmt.Println("用户手机号为:", phoneInfo.PhoneNumber)
openid := wxResp.OpenID
ctx := c.Request.Context() // 获取请求上下文,用于控制数据库操作超时
var username string
// 1. 查询用户是否存在
var exists bool
query := "SELECT EXISTS(SELECT 1 FROM user_info WHERE uid = ? LIMIT 1)"
err = databaseInit.UserDB.QueryRowContext(ctx, query, openid).Scan(&exists)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "查询用户存在性失败: " + err.Error(),
"code": "10029",
})
return
}
// 2. 如果用户不存在,插入新用户
if !exists {
username = generateUsername()
now := time.Now()
insertSQL := `
INSERT INTO user_info (
uid, gender,createdtime, updatedtime,username,telephone
) VALUES (?, ?, ?,?,?,?)
`
_, err := databaseInit.UserDB.ExecContext(
ctx,
insertSQL,
openid, // uid使用微信 openid
2,
now, // createdtime
now, // updatedtime
username,
phoneInfo.PhoneNumber,
)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "插入新用户失败: " + err.Error(),
"code": "10029",
})
return
}
} else {
queryUser := "SELECT username FROM user_info WHERE uid = ? LIMIT 1"
err = databaseInit.UserDB.QueryRowContext(ctx, queryUser, openid).Scan(&username)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "查询用户信息失败: " + err.Error(),
"code": "10030",
})
return
}
}
token, err := utill.GenerateJWTAndStore(openid)
if err != nil {
c.JSON(500, gin.H{"error": "生成令牌失败", "code": "10036"})
return
}
c.JSON(http.StatusOK, gin.H{"result": "success", "error": nil, "code": "20001", "token": token,
"userinfo": map[string]string{
"username": username,
"uid": openid,
"telephone": phoneInfo.PhoneNumber,
},
})
}
func generateUsername() string {
// 初始化随机数种子(确保每次运行生成不同随机数)
rand.Seed(time.Now().UnixNano())
// 生成8位随机数字范围10000000-99999999
randomNum := 10000000 + rand.Intn(90000000) // 8位随机数
return "用户" + strconv.Itoa(randomNum)
}
func decryptWxData(sessionKey, encryptedData, iv string) ([]byte, error) {
key, _ := base64.StdEncoding.DecodeString(sessionKey)
data, _ := base64.StdEncoding.DecodeString(encryptedData)
ivBytes, _ := base64.StdEncoding.DecodeString(iv)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, ivBytes)
mode.CryptBlocks(data, data)
return pkcs7Unpad(data), nil // 返回去除填充后的原始数据
}
// 去除PKCS#7填充微信加密数据使用PKCS#7填充
func pkcs7Unpad(data []byte) []byte {
if len(data) == 0 {
return nil
}
padding := int(data[len(data)-1])
if padding > len(data) {
return nil
}
return data[:len(data)-padding]
}