fwh_login.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package fwh_pro
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. )
  7. var loginInfo LoginData = LoginData{
  8. UserId: 0,
  9. Token: "",
  10. }
  11. type LoginPayload struct {
  12. LoginName string `json:"loginName"`
  13. Password string `json:"password"`
  14. Type int `json:"type"`
  15. Lang string `json:"lang"`
  16. }
  17. type LoginData struct {
  18. UserId uint `json:"userId"`
  19. Token string `json:"token"`
  20. }
  21. type LoginResponse struct {
  22. Code int `json:"code"`
  23. Msg string `json:"msg"`
  24. Data LoginData `json:"data"`
  25. Success bool `json:"success"`
  26. }
  27. type JwtPayload struct {
  28. AdminLevel int `json:"adminLevel"`
  29. LoginName string `json:"loginName"`
  30. AccountType int `json:"accountType"`
  31. GroupId int `json:"groupId"`
  32. Iss string `json:"iss"`
  33. UserManage int `json:"userManage"`
  34. Exp uint64 `json:"exp"`
  35. UserName string `json:"userName"`
  36. Iat uint64 `json:"iat"`
  37. UserId int `json:"userId"`
  38. }
  39. func Login() error {
  40. // A failed re-login must not leave a previously issued token available for reuse.
  41. ClearToken()
  42. // 要发送的 JSON
  43. payload := LoginPayload{
  44. LoginName: fwhUser,
  45. Password: fwhPassword,
  46. Type: 1,
  47. Lang: "ZH_CN",
  48. }
  49. var body any = payload
  50. if fwhTotpSecret != "" {
  51. if fwhTotpField == "" {
  52. return fmt.Errorf("FWH_TOTP_FIELD is required when FWH_TOTP_SECRET is set")
  53. }
  54. if fwhTotpField == "loginName" || fwhTotpField == "password" || fwhTotpField == "type" || fwhTotpField == "lang" {
  55. return fmt.Errorf("FWH_TOTP_FIELD conflicts with an existing login field")
  56. }
  57. code, err := generateTOTP(fwhTotpSecret, time.Now())
  58. if err != nil {
  59. return err
  60. }
  61. body = map[string]any{
  62. "loginName": payload.LoginName,
  63. "password": payload.Password,
  64. "type": payload.Type,
  65. "lang": payload.Lang,
  66. fwhTotpField: code,
  67. }
  68. }
  69. response, err := FwhHttpPostWithoutToken(getFullOssUrl("/api-user/user/login"), body)
  70. if err != nil {
  71. return err
  72. }
  73. // logger.Std.Printf("Status: %s\nBody: %s", resp.Status, body)
  74. var loginRes LoginResponse
  75. // 反序列化(unmarshal)
  76. jsonErr := json.Unmarshal(response.Body, &loginRes)
  77. if jsonErr != nil {
  78. return jsonErr
  79. }
  80. if response.StatusCode != 200 || !loginRes.Success || loginRes.Data.Token == "" {
  81. return fmt.Errorf("FWH login rejected (HTTP %d, code %d)", response.StatusCode, loginRes.Code)
  82. }
  83. loginInfo.UserId = loginRes.Data.UserId
  84. loginInfo.Token = loginRes.Data.Token
  85. return nil
  86. }
  87. // 弃用:token payload被加密
  88. // func isTokenExpired(token string) (bool, error) {
  89. // parts := strings.Split(token, ".")
  90. // if len(parts) != 3 {
  91. // return false, fmt.Errorf("token 格式错误")
  92. // }
  93. // payloadSegment := parts[1]
  94. // // JWT 使用 base64 URL encoding
  95. // payloadBytes, err := base64.RawURLEncoding.DecodeString(payloadSegment)
  96. // if err != nil {
  97. // return false, err
  98. // }
  99. // var payload map[string]interface{}
  100. // if err := json.Unmarshal(payloadBytes, &payload); err != nil {
  101. // zap.L().Error("token payload解析失败", zap.String("payload", string(payloadBytes)))
  102. // return false, err
  103. // }
  104. // expFloat, ok := payload["exp"].(float64)
  105. // if !ok {
  106. // return false, fmt.Errorf("token payload 中没有 exp")
  107. // }
  108. // expTime := time.Unix(int64(expFloat), 0)
  109. // return time.Now().After(expTime), nil
  110. // }
  111. func getLoginToken() string {
  112. // 判断是否已经有token
  113. if loginInfo.Token != "" {
  114. // 判断token是否过期
  115. // token.payload加密,无法判断过期,改为判断401
  116. // expired, err := isTokenExpired(loginInfo.Token)
  117. // if err != nil {
  118. // zap.L().Error("loginInfo.Token 解析出错.", zap.String("token", loginInfo.Token), zap.String("err", err.Error()))
  119. // return ""
  120. // }
  121. // if expired {
  122. // zap.L().Info("fwh Token 已过期")
  123. // } else {
  124. // Token 有效
  125. return loginInfo.Token
  126. // }
  127. }
  128. // 重新登录
  129. Login()
  130. return loginInfo.Token
  131. }
  132. // 弃用:token payload被加密
  133. // func getUserId(token string) (int, error) {
  134. // // JWT 由三部分组成: header.payload.signature
  135. // parts := strings.Split(token, ".")
  136. // if len(parts) < 2 {
  137. // return 0, fmt.Errorf("getUserId: invalid token")
  138. // }
  139. // // 解码 payload 部分
  140. // payload, err := base64.RawURLEncoding.DecodeString(parts[1])
  141. // if err != nil {
  142. // return 0, fmt.Errorf("getUserId: %s", err.Error())
  143. // }
  144. // // 解析 JSON
  145. // var claims JwtPayload
  146. // if err := json.Unmarshal(payload, &claims); err != nil {
  147. // return 0, fmt.Errorf("getUserId: %s", err.Error())
  148. // }
  149. // return claims.UserId, nil
  150. // }
  151. func getUserId() uint {
  152. return loginInfo.UserId
  153. }
  154. // 清空token
  155. func ClearToken() {
  156. loginInfo.UserId = 0
  157. loginInfo.Token = ""
  158. }
  159. // 过期重新登录 - 提供给httpClient
  160. func reLogin() string {
  161. // 重新登录
  162. Login()
  163. return loginInfo.Token
  164. }