之前做项目的时候为了省事都是直接采用第三方的 Auth 包。这次使用 Gin 实现时决定参考去年写的 JWT 文章实现一个简单的 token 认证,如果存在安全漏洞或者不周到的地方烦请指出。
  思路很简单,服务器验证账号和密码后,将拼接的用户名与过期时间作为 Payload,使用 HMACSHA256 对拼接后的 Payload 进行签名。再与 Base64 编码后的 Payload 进行拼接。token 结构为 SignaturePayload.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | const secret = "Secretkey"
 const exp = 31536000
 
 
 func EncodeToken(username string) string {
 
 expireTime := time.Now().Unix() + exp
 
 data := username + "." + strconv.FormatInt(expireTime, 10)
 
 base64Data := base64.StdEncoding.EncodeToString([]byte(data))
 
 mac := hmac.New(sha256.New, []byte(secret))
 mac.Write([]byte(data))
 expectedMAC := hex.EncodeToString(mac.Sum(nil))
 
 token := expectedMAC + base64Data
 return token
 }
 
 | 
  用户之后的访问携带该 token,服务器将 Base64 编码的 Payload 解码,使用 HMACSHA256 签名并与 Signature 部分验证。如果通过验证,判断用户名是否存在和 token 是否过期。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | func VerifyToken(token string, username *string) bool {
 messageMAC := token[:64]
 base64data := token[64:]
 
 data, err := base64.StdEncoding.DecodeString(base64data)
 if err != nil {
 return false
 }
 
 mac := hmac.New(sha256.New, []byte(secret))
 mac.Write(data)
 if messageMAC == hex.EncodeToString(mac.Sum(nil)) {
 
 *username = strings.Split(string(data), ".")[0]
 expireTime, _ := strconv.ParseInt(strings.Split(string(data), ".")[1], 10, 64)
 if controller.HavingUser(*username) && expireTime > time.Now().Unix() {
 return true
 }
 return false
 }
 return false
 }
 
 |