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 <do4suki@gmail.com>
This commit is contained in:
2023-04-11 17:32:17 +08:00
parent 58818e6833
commit bb8a8e502d
3 changed files with 272 additions and 84 deletions
+39 -28
View File
@@ -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)
+231 -56
View File
@@ -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)