From bb8a8e502dba1aa3b8faf390adac5ef54788f156 Mon Sep 17 00:00:00 2001 From: Yuan Si Date: Tue, 11 Apr 2023 17:31:37 +0800 Subject: [PATCH] Support account management CREATE TABLE "users" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "phone" TEXT, "password" TEXT, "autokey" TEXT, "ticket" TEXT, "userid" INTEGER, "last_login_time" INTEGER ); CREATE TABLE "user_key" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "userid" INTEGER, "key" INTEGER ); Signed-off-by: Yuan Si --- .gitignore | 2 + handler/login.go | 67 ++++++----- handler/private.go | 287 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 272 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index 15bf8c0..5a93bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ publickey.pem data logs +assets/account.db + assets/api1.json assets/api2.json assets/api3.json diff --git a/handler/login.go b/handler/login.go index 63d0625..f49673f 100644 --- a/handler/login.go +++ b/handler/login.go @@ -1,6 +1,7 @@ package handler import ( + "database/sql" "encoding/base64" "encoding/json" "fmt" @@ -13,6 +14,8 @@ import ( "time" "github.com/gin-gonic/gin" + _ "github.com/mattn/go-sqlite3" + "github.com/tidwall/gjson" ) func AuthKeyHandler(ctx *gin.Context) { @@ -74,36 +77,34 @@ func LoginHandler(ctx *gin.Context) { reqTime := time.Now().Unix() authorizeStr := ctx.Request.Header["Authorize"] - // authToken, err := utils.GetAuthorizeToken(authorizeStr) - // if err != nil { - // ctx.String(http.StatusForbidden, ErrorMsg) - // return - // } + authToken, err := utils.GetAuthorizeToken(authorizeStr) + if err != nil { + ctx.String(http.StatusForbidden, ErrorMsg) + return + } - // authData, err := database.RedisCli.HGetAll(database.RedisCtx, authToken).Result() - // authData, err := database.LevelDb.Get([]byte(authToken)) - // CheckErr(err) + authData, err := database.LevelDb.Get([]byte(authToken)) + CheckErr(err) // fmt.Println(authData) - // clientToken, serverToken := authData["client_token"], authData["server_token"] - // clientToken := gjson.Get(string(authData), "client_token").String() - // serverToken := gjson.Get(string(authData), "server_token").String() - // clientToken64, err := base64.RawStdEncoding.DecodeString(clientToken) - // CheckErr(err) - // serverToken64, err := base64.RawStdEncoding.DecodeString(serverToken) - // CheckErr(err) + clientToken := gjson.Get(string(authData), "client_token").String() + serverToken := gjson.Get(string(authData), "server_token").String() + clientToken64, err := base64.RawStdEncoding.DecodeString(clientToken) + CheckErr(err) + serverToken64, err := base64.RawStdEncoding.DecodeString(serverToken) + CheckErr(err) - // xmcKey := utils.SliceXor([]byte(clientToken64), []byte(serverToken64)) - // aesKey := xmcKey[0:16] + xmcKey := utils.SliceXor([]byte(clientToken64), []byte(serverToken64)) + aesKey := xmcKey[0:16] loginReq := model.LoginReq{} - err := json.Unmarshal([]byte(ctx.PostForm("request_data")), &loginReq) + err = json.Unmarshal([]byte(ctx.PostForm("request_data")), &loginReq) + CheckErr(err) + key64, err := base64.StdEncoding.DecodeString(loginReq.LoginKey) CheckErr(err) - // key64, err := base64.StdEncoding.DecodeString(loginReq.LoginKey) - // CheckErr(err) // pass64, err := base64.StdEncoding.DecodeString(loginReq.LoginPasswd) // CheckErr(err) - // keyDescrypted := utils.Sub16(encrypt.AES_CBC_Decrypt(key64, aesKey)) + keyDescrypted := utils.Sub16(encrypt.AES_CBC_Decrypt(key64, aesKey)) // fmt.Println(string(keyDescrypted)) // passDescrypted := utils.Sub16(encrypt.AES_CBC_Decrypt(pass64, aesKey)) // fmt.Println(string(passDescrypted)) @@ -116,16 +117,26 @@ func LoginHandler(ctx *gin.Context) { } nonce++ - // userId, err := database.GetUid(string(keyDescrypted)) - // if err != nil { - // ctx.String(http.StatusForbidden, ErrorMsg) - // return - // } - userId := 9999999 + db, err := sql.Open("sqlite3", "assets/account.db") + CheckErr(err) + defer db.Close() + + stmt, err := db.Prepare("SELECT userid FROM user_key WHERE key = ?") + CheckErr(err) + rows, err := stmt.Query(string(keyDescrypted)) + CheckErr(err) + var userId int + for rows.Next() { + err = rows.Scan(&userId) + CheckErr(err) + } + + if userId == 0 { + userId = 9999999 + } sUserId := strconv.Itoa(userId) authorizeToken := utils.RandomBase64Token(32) - // _, err = database.RedisCli.HSet(database.RedisCtx, "token_uid", authorizeToken, userId).Result() err = database.LevelDb.Put([]byte(authorizeToken), []byte(sUserId)) CheckErr(err) diff --git a/handler/private.go b/handler/private.go index 72275b3..36375e4 100644 --- a/handler/private.go +++ b/handler/private.go @@ -1,23 +1,62 @@ package handler import ( + "database/sql" "encoding/base64" + "encoding/json" "fmt" "honoka-chan/encrypt" "honoka-chan/utils" "io" "net/http" "net/url" + "strconv" "strings" + "time" "github.com/forgoer/openssl" "github.com/gin-gonic/gin" + _ "github.com/mattn/go-sqlite3" ) var ( randKey string ) +type LoginResp struct { + Activation int `json:"activation"` + Autokey string `json:"autokey"` + CaptchaParams string `json:"captchaParams"` + CheckCodeGUID string `json:"checkCodeGuid"` + CheckCodeURL string `json:"checkCodeUrl"` + HasExtendAccs int `json:"hasExtendAccs"` + HasRealInfo int `json:"has_realInfo"` + ImagecodeType int `json:"imagecodeType"` + IsNewUser int `json:"isNewUser"` + Message string `json:"message"` + NextAction int `json:"nextAction"` + PromptMsg string `json:"prompt_msg"` + RealInfoNotification string `json:"realInfoNotification"` + RealInfoForce int `json:"realInfo_force"` + RealInfoForcePay int `json:"realInfo_force_pay"` + RealInfoStatus int `json:"realInfo_status"` + RealInfoStatusPay int `json:"realInfo_status_pay"` + Result int `json:"result"` + SdgHeight int `json:"sdg_height"` + SdgWidth int `json:"sdg_width"` + Ticket string `json:"ticket"` + UserAttribute string `json:"userAttribute"` + Userid string `json:"userid"` +} + +type LoginAutoResp struct { + Result int `json:"result"` + Message string `json:"message"` + Autokey string `json:"autokey"` + Userid string `json:"userid"` + Ticket string `json:"ticket"` +} + func ActiveHandler(ctx *gin.Context) { ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, `{ "code": 0, "msg": "ok", "data": { "message": "ok", "result": 0 } }`) @@ -132,27 +171,70 @@ func GetCodeHandler(ctx *gin.Context) { } func LoginAutoHandler(ctx *gin.Context) { - // body, err := io.ReadAll(ctx.Request.Body) - // CheckErr(err) - // defer ctx.Request.Body.Close() + body, err := io.ReadAll(ctx.Request.Body) + CheckErr(err) + defer ctx.Request.Body.Close() // fmt.Println(string(body)) - // body64, err := base64.StdEncoding.DecodeString(string(body)) - // CheckErr(err) + body64, err := base64.StdEncoding.DecodeString(string(body)) + CheckErr(err) // fmt.Println(string(body64)) - // decryptedBody, err := openssl.Des3ECBDecrypt(body64, []byte(randKey)[0:24], openssl.PKCS7_PADDING) - // CheckErr(err) - // fmt.Println(string(decryptedBody)) - - // Unable to decrypt server data - token := `{"message":"Hello, world!"}` - encryptedToken, err := openssl.Des3ECBEncrypt([]byte(token), []byte(randKey)[0:24], openssl.PKCS7_PADDING) + decryptedBody, err := openssl.Des3ECBDecrypt(body64, []byte(randKey)[0:24], openssl.PKCS7_PADDING) CheckErr(err) - encryptedToken64 := base64.StdEncoding.EncodeToString(encryptedToken) - // fmt.Println(encryptedToken64) + queryStr := string(decryptedBody) + // fmt.Println(params) - resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedToken64) + params, err := url.ParseQuery(queryStr) + CheckErr(err) + autoKey := params.Get("autokey") + // fmt.Println(autoKey) + if autoKey == "" { + ctx.String(http.StatusForbidden, ErrorMsg) + return + } + + db, err := sql.Open("sqlite3", "assets/account.db") + CheckErr(err) + defer db.Close() + + stmt, err := db.Prepare("SELECT userid,ticket AS ct FROM users WHERE autokey = ?") + CheckErr(err) + rows, err := stmt.Query(autoKey) + CheckErr(err) + var userid, ticket string + for rows.Next() { + err = rows.Scan(&userid, &ticket) + CheckErr(err) + } + + var resp string + if userid != "" { + autoResp := LoginAutoResp{ + Result: 0, + Message: "ok", + Autokey: autoKey, + Userid: userid, + Ticket: ticket, + } + data, err := json.Marshal(autoResp) + // fmt.Println(string(data)) + CheckErr(err) + encryptedData, err := openssl.Des3ECBEncrypt(data, []byte(randKey)[0:24], openssl.PKCS7_PADDING) + CheckErr(err) + encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) + // fmt.Println(encryptedData64) + + resp = fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedData64) + } else { + data := `{"message":"账号不存在或者登陆状态已过期!","result":31}` + encryptedData, err := openssl.Des3ECBEncrypt([]byte(data), []byte(randKey)[0:24], openssl.PKCS7_PADDING) + CheckErr(err) + encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) + // fmt.Println(encryptedData64) + + resp = fmt.Sprintf(`{ "code": 31, "msg": "账号不存在或者登陆状态已过期!", "data": "%s" }`, encryptedData64) + } ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) @@ -161,7 +243,7 @@ func LoginAutoHandler(ctx *gin.Context) { func LoginAreaHandler(ctx *gin.Context) { userId := ctx.PostForm("userid") if userId != "" { - fmt.Println(userId) + // fmt.Println(userId) resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": { "userid": "%s" } }`, userId) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) @@ -169,53 +251,146 @@ func LoginAreaHandler(ctx *gin.Context) { } func AccountLoginHandler(ctx *gin.Context) { - // body, err := io.ReadAll(ctx.Request.Body) - // CheckErr(err) - // defer ctx.Request.Body.Close() + body, err := io.ReadAll(ctx.Request.Body) + CheckErr(err) + defer ctx.Request.Body.Close() // fmt.Println(string(body)) - // body64, err := base64.StdEncoding.DecodeString(string(body)) - // CheckErr(err) + body64, err := base64.StdEncoding.DecodeString(string(body)) + CheckErr(err) // fmt.Println(string(body64)) - // decryptedBody, err := openssl.Des3ECBDecrypt(body64, []byte(randKey)[0:24], openssl.PKCS7_PADDING) - // CheckErr(err) - // fmt.Println(string(decryptedBody)) - - // userid: 实际登录用的账号 - // ticket: 实际登录用的密码(每次登录都会重新生成新的) - // 注意:更换设备(deviceId 发生变化)会重新生成 autokey - data := `{ - "activation":0, - "autokey":"AUTOB36C64BA1CC84E37AC5694FD7BE6652F", - "captchaParams":"", - "checkCodeGuid":"", - "checkCodeUrl":"", - "hasExtendAccs":0, - "has_realInfo":1, - "imagecodeType":0, - "isNewUser":0, - "message":"ok", - "nextAction":0, - "prompt_msg":"", - "realInfoNotification":"", - "realInfo_force":1, - "realInfo_force_pay":0, - "realInfo_status":0, - "realInfo_status_pay":0, - "result":0, - "sdg_height":0, - "sdg_width":0, - "ticket":"810171225920071361680690948", - "userAttribute":"0", - "userid":"2592007136" - }` - encryptedToken, err := openssl.Des3ECBEncrypt([]byte(data), []byte(randKey)[0:24], openssl.PKCS7_PADDING) + decryptedBody, err := openssl.Des3ECBDecrypt(body64, []byte(randKey)[0:24], openssl.PKCS7_PADDING) CheckErr(err) - encryptedToken64 := base64.StdEncoding.EncodeToString(encryptedToken) + queryStr, err := url.QueryUnescape(string(decryptedBody)) + CheckErr(err) + + params, err := url.ParseQuery(queryStr) + CheckErr(err) + + phone, password := params.Get("phone"), params.Get("password") + if phone == "" || password == "" { + ctx.String(http.StatusForbidden, ErrorMsg) + return + } + + // sql := `CREATE TABLE "users" ( + // "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + // "phone" TEXT, + // "password" TEXT, + // "autokey" TEXT, + // "ticket" TEXT, + // "userid" INTEGER, + // "last_login_time" INTEGER + // ); + // CREATE TABLE "user_key" ( + // "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + // "userid" INTEGER, + // "key" INTEGER + // );` + db, err := sql.Open("sqlite3", "assets/account.db") + CheckErr(err) + defer db.Close() + + stmt, err := db.Prepare("SELECT password,autokey,ticket,userid FROM users WHERE phone = ?") + CheckErr(err) + rows, err := stmt.Query(phone) + CheckErr(err) + var pass, autokey, ticket, userid string + for rows.Next() { + err = rows.Scan(&pass, &autokey, &ticket, &userid) + CheckErr(err) + } + loginResp := LoginResp{} + loginCode := 0 + loginMsg := "ok" + loginTime := time.Now().Unix() + if pass == "" { + // 未注册 - 自动注册 + trans, err := db.Begin() + if err != nil { + trans.Rollback() + panic(err) + } + pass = openssl.Md5ToString(password) + autokey = "AUTO" + strings.ToUpper(utils.RandomStr(32)) + userid = strconv.Itoa(int(loginTime)) + ticket = "9999999" + userid + userid + stmt, err = trans.Prepare("INSERT INTO users(phone,password,autokey,ticket,userid,last_login_time) VALUES (?,?,?,?,?,?)") + if err != nil { + trans.Rollback() + panic(err) + } + _, err = stmt.Exec(phone, pass, autokey, ticket, userid, loginTime) + if err != nil { + trans.Rollback() + panic(err) + } + // id, _ := res.LastInsertId() + // fmt.Println("LastInsertId:", id) + + stmt, err = trans.Prepare("INSERT INTO user_key(userid,key) VALUES(?,?)") + if err != nil { + trans.Rollback() + panic(err) + } + // 方便起见初始化 userid 和 key 一样 + // 注意:user_key 表中的 key 是上文生成的用于登录的 userid,而 userid 则是用于 Authorize Token 生成用的 + _, err = stmt.Exec(userid, userid) + if err != nil { + trans.Rollback() + panic(err) + } + + if err = trans.Commit(); err != nil { + trans.Rollback() + panic(err) + } + + // Login Response + loginResp.Autokey = autokey + loginResp.HasRealInfo = 1 + loginResp.Message = "ok" + loginResp.RealInfoForce = 1 + loginResp.Ticket = ticket + loginResp.UserAttribute = "0" + loginResp.Userid = userid + } else { + // 已注册 + if openssl.Md5ToString(password) == pass { + // 密码正确 + // Login Response + loginResp.Autokey = autokey // 注意:更换设备(deviceId 发生变化)应重新生成 autokey + loginResp.HasRealInfo = 1 + loginResp.Message = "ok" + loginResp.RealInfoForce = 1 + loginResp.Ticket = "9999999" + userid + strconv.Itoa(int(loginTime)) // 实际登录用的密码(每次登录都会重新生成新的) + loginResp.UserAttribute = "0" + loginResp.Userid = userid // 实际登录用的账号 + + // 更新信息 + stmt, err = db.Prepare("UPDATE users SET autokey=?,ticket=?,last_login_time=? WHERE userid=?") + CheckErr(err) + _, err := stmt.Exec(autokey, ticket, loginTime, userid) + CheckErr(err) + // aff, _ := res.RowsAffected() + // fmt.Println("RowsAffected:", aff) + } else { + // 密码错误 + loginCode = 31 + loginMsg = "账号不存在或者密码有误!" + } + } + + data, err := json.Marshal(loginResp) + CheckErr(err) + // fmt.Println(string(data)) + encryptedData, err := openssl.Des3ECBEncrypt(data, []byte(randKey)[0:24], openssl.PKCS7_PADDING) + CheckErr(err) + encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) // fmt.Println(encryptedToken64) - resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedToken64) + resp := fmt.Sprintf(`{ "code": %d, "msg": %s, "data": "%s" }`, loginCode, loginMsg, encryptedData64) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp)