用户 cookie 验证随机失败

huangapple go评论120阅读模式
英文:

User cookie verification fails randomly

问题

我正在使用两个结构体来保存用户信息:

// SecureDevice保存用户设备的信息
type SecureDevice struct {
Name string // 用户定义的名称
DeviceIP string
Token struct {
Token string
StartingDate time.Time // 令牌只能在一周后失效
}
}

// GlobalUser是一个定义了服务器中所有用户信息的结构体
type GlobalUser struct {
Username string
Password string
Salt string
Mail string
ValidationToken string // 用于验证用户的邮箱地址
Lang string
ConversationsID []int // 用户参与的私人消息
SecureDevicesList []SecureDevice
}

我使用一个函数来检查用户是否已登录:

// IsLoggedIn检查客户端的令牌是否有效
func IsLoggedIn(r *http.Request) string {
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
ip := ips[0]
cookie, err := r.Cookie("auth")
if err != nil {
return "ERR$" + "not_connected"
}
cookieValue := strings.Split(cookie.Value, "$")
println(cookie.Value)
user := GetUser(cookieValue[0])
userToken := cookieValue1
if user.Username == "" {
return "ERR$" + "error"
}
for _, SecureDevice := range user.SecureDevicesList {
if SecureDevice.DeviceIP == ip && SecureDevice.Token.Token == userToken { // 确保提供的令牌是用户的令牌
if time.Since(SecureDevice.Token.StartingDate)*time.Hour >= 168 { // 如果令牌超过1周,丢弃它
return "ERR$" + "error_token_expired"
} else if time.Since(SecureDevice.Token.StartingDate)*time.Second >= 30 { // 如果令牌的年龄在1小时到1周之间,更新令牌
db, err := scribble.New("./brony/db", nil)
if err != nil {
return "ERR$" + "error_internal"
}
tokenBytes, err := GenerateRandomBytes(64) // 生成一个令牌
if err != nil {
return "ERR$" + "error_internal"
}
token := base64.URLEncoding.EncodeToString(tokenBytes)
SecureDevice.Token.Token = token
SecureDevice.Token.StartingDate = time.Now()
errr := db.Write("users", user.Username, user)
if errr != nil {
return "ERR$" + "error_internal"
}
return "TOK$" + user.Username + "$" + SecureDevice.Token.Token
} else if time.Since(SecureDevice.Token.StartingDate)*time.Hour <= 1 {
return "NIL$"
}
} else if SecureDevice.DeviceIP == ip {
return "ERR$" + "error_bad_token"
}
}
return "ERR$" + "error_device_not_registered"
}

但是,当我加载页面时,几乎总是会出现错误:

status := IsLoggedIn(r)
println(status)

由于我正在打印"IsLoggedIn"以了解问题的来源,所以经常会出现错误。

起初我以为是我的令牌更新代码有问题,但是尽管有问题且未完成,但似乎并不是它的问题,因为经过几次刷新后,它说cookie是正常的。我真的不明白问题出在哪里,这真的很烦人,因为我不能就这样放任不管,对用户来说,每次按F5并希望页面刷新时会奇迹般地工作,这将非常令人恼火。代码在Debian服务器上运行。

英文:

I'm using two structs to hold user's info

  1. // SecureDevice holds a user&#39;s device&#39;s infos
  2. type SecureDevice struct {
  3. Name string // Defined by the user
  4. DeviceIP string
  5. Token struct {
  6. Token string
  7. StartingDate time.Time // The token is supposed to last only a week before becoming invalid
  8. }
  9. }
  10. // GlobalUser is a struct defining all user&#39;s infos registered inside the server
  11. type GlobalUser struct {
  12. Username string
  13. Password string
  14. Salt string
  15. Mail string
  16. ValidationToken string // Used to validate the user&#39;s mail adress
  17. Lang string
  18. ConversationsID []int // The private messages the user has part in
  19. SecureDevicesList []SecureDevice
  20. }

And I'm using a function to check if the user is logged in

  1. // IsLoggedIn checks if client&#39;s token is valid
  2. func IsLoggedIn(r *http.Request) string {
  3. ips := strings.Split(r.Header.Get(&quot;X-Forwarded-For&quot;), &quot;, &quot;)
  4. ip := ips[0]
  5. cookie, err := r.Cookie(&quot;auth&quot;)
  6. if err != nil {
  7. return &quot;ERR$&quot; + &quot;not_connected&quot;
  8. }
  9. cookieValue := strings.Split(cookie.Value, &quot;$&quot;)
  10. println(cookie.Value)
  11. user := GetUser(cookieValue[0])
  12. userToken := cookieValue[1]
  13. if user.Username == &quot;&quot; {
  14. return &quot;ERR$&quot; + &quot;error&quot;
  15. }
  16. for _, SecureDevice := range user.SecureDevicesList {
  17. if SecureDevice.DeviceIP == ip &amp;&amp; SecureDevice.Token.Token == userToken { // We make sure that the token provided is actually the user&#39;s token
  18. if time.Since(SecureDevice.Token.StartingDate)*time.Hour &gt;= 168 { // If token is older than 1 week, we throw it away
  19. return &quot;ERR$&quot; + &quot;error_token_expired&quot;
  20. } else if time.Since(SecureDevice.Token.StartingDate)*time.Second &gt;= 30 { // If it&#39;s age is between 1 hour and one week, we renew it
  21. db, err := scribble.New(&quot;./brony/db&quot;, nil)
  22. if err != nil {
  23. return &quot;ERR$&quot; + &quot;error_internal&quot;
  24. }
  25. tokenBytes, err := GenerateRandomBytes(64) // Generates a salt
  26. if err != nil {
  27. return &quot;ERR$&quot; + &quot;error_internal&quot;
  28. }
  29. token := base64.URLEncoding.EncodeToString(tokenBytes)
  30. SecureDevice.Token.Token = token
  31. SecureDevice.Token.StartingDate = time.Now()
  32. errr := db.Write(&quot;users&quot;, user.Username, user)
  33. if errr != nil {
  34. return &quot;ERR$&quot; + &quot;error_internal&quot;
  35. }
  36. return &quot;TOK$&quot; + user.Username + &quot;$&quot; + SecureDevice.Token.Token
  37. } else if time.Since(SecureDevice.Token.StartingDate)*time.Hour &lt;= 1 {
  38. return &quot;NIL$&quot;
  39. }
  40. } else if SecureDevice.DeviceIP == ip {
  41. return &quot;ERR$&quot; + &quot;error_bad_token&quot;
  42. }
  43. }
  44. return &quot;ERR$&quot; + &quot;error_device_not_registered&quot;
  45. }

But almost always when I load the page with

  1. status := IsLoggedIn(r)
  2. println(status)

It often gives me an error, since I'm printing "IsLoggedIn" to understand where the problem comes from

  1. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  2. NIL$
  3. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  4. ERR$error_token_expired
  5. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  6. NIL$
  7. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  8. ERR$error_token_expired
  9. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  10. ERR$error_token_expired
  11. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  12. ERR$error_token_expired
  13. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  14. ERR$error_token_expired
  15. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  16. ERR$error_token_expired
  17. test$ppDXRggtztyA9OBbdZh1t1ESqRo2XvuOBt4xlDai9kVxwq-_3zlWyvgNgA7AZcSpasJ_YnXZvoG qlz1syF9X8g==
  18. NIL$

At first I thought it was my token's renewal code which was faulty, but while beeing faulty and unfinished, it doesn't seem to be it's fault since after a few f5, it said that the cookie was ok. I really don't understand where the fault is, and it's starting to get really annoying, as I can't just let it be, it would be very annoying to say the least for a user to do f5 everytime and hope that when the page will refresh, it will miraculously work. The code runs on a debian server

答案1

得分: 0

你的问题可能是你处理 time.Since 返回值进行比较的方式不正确。

time.Since 方法返回 Duration 类型,并且内部表示为 int64 类型。值以纳秒为单位。

尝试这样做:

  1. elapsedHours := int64(time.Since(SecureDevice.Token.StartingDate).Hours())
  2. if elapsedHours >= 168 {
  3. //...
  4. } else if elapsedHours >= 30 {
  5. //...
  6. } else if elapsedHours <= 1 {
  7. //...
  8. }
英文:

Your issue might be, the way you handle time.Since return value for comparison.

time.Since method returns type Duration and internally represented as type int64. Value is in Nanoseconds.

Try this-

  1. elapsedHours := int64(time.Since(SecureDevice.Token.StartingDate).Hours())
  2. if elapsedHours &gt;= 168 {
  3. //...
  4. } else if elapsedHours &gt;= 30 {
  5. //...
  6. } else if elapsedHours &lt;= 1 {
  7. //...
  8. }

huangapple
  • 本文由 发表于 2017年7月2日 00:55:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/44863386.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定