之前做项目的时候为了省事都是直接采用第三方的 Auth 包。这次使用 Gin 实现时决定参考去年写的 JWT 文章实现一个简单的 token 认证,如果存在安全漏洞或者不周到的地方烦请指出。
思路很简单,服务器验证账号和密码后,将拼接的用户名与过期时间作为 Payload
,使用 HMACSHA256 对拼接后的 Payload
进行签名。再与 Base64 编码后的 Payload
进行拼接。token 结构为 SignaturePayload
.
1 2 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
是否过期。
1 2 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 }
|