Go 实现 OAuth 2.0 Access Token & Refresh Token
May 17, 2022
Go 实现 OAuth 2.0 Access Token & Refresh Token
Access Token
- 过期时间短,过期后或濒临过期时,需要前端使用
refresh token
去向后端获取新的access token
- 在需要用户鉴权的业务中,使用该
token
鉴权,正因为该token
的过期时间短,所以不易被复用滥用
Refresh Token
- 过期时间长,不用于业务请求中,使用该
token
获取access_token
Go 实现
package server
import (
"github.com/dgrijalva/jwt-go"
"time"
)
var AccessSecret = "zyy4nj5o6Zk7oS70sQFSAFQIHCZMISfplH5Oil5WFV4WWDVoYEun2EJdgqvs"
var RefreshSecret = "eYDVVTMjGh8cuemIceKEFsDoZxoCdlQM9YPef7nU9u5ndyd1xrijlsNe9TMi"
type (
CustomClaims struct {
UserID uint
jwt.StandardClaims
}
ClientToken struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
)
// 获取新token
func GetClientToken(userID uint) (*ClientToken, error) {
accessTokenSigned, err := generateToken(userID, time.Now().Add(15*time.Minute).Unix(), AccessSecret)
if err != nil {
return nil, err
}
refreshTokenSigned, err := generateToken(userID, time.Now().Add(time.Hour*24*7).Unix(), RefreshSecret)
if err != nil {
return nil, err
}
return &ClientToken{
AccessToken: accessTokenSigned,
RefreshToken: refreshTokenSigned,
}, nil
}
// 刷新access token
func RefreshToken(refreshToken string) (string, error) {
tokenClaims, err := ParseToken(refreshToken, RefreshSecret)
if err != nil {
return "", err
}
id := tokenClaims.UserID
accessToken, err := generateToken(id, time.Now().Add(15*time.Minute).Unix(), AccessSecret)
if err != nil {
return "", err
}
return accessToken, nil
}
// 解析token
func ParseToken(token string, secret string) (*CustomClaims, error) {
tokenClaims, err := jwt.ParseWithClaims(token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := tokenClaims.Claims.(*CustomClaims); ok && tokenClaims.Valid {
return claims, nil
}
return nil, err
}
// 内部生成Token逻辑
func generateToken(userID uint, expiresAt int64, secret string) (string, error) {
customClaims := CustomClaims{
userID,
jwt.StandardClaims{
Issuer: "Antmons",
IssuedAt: time.Now().Unix(),
ExpiresAt: expiresAt,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, customClaims)
return token.SignedString([]byte(secret))
}
Gin 中间件
func TokenAuthMiddleware(c *gin.Context) {
headerToken := c.GetHeader("authorization")
token := strings.ReplaceAll(headerToken, "Bearer ", "")
claim, err := ParseToken(token, AccessSecret)
if err != nil {
serverErrorTypeCustom(c, 500, "access token is expires", err)
return
}
c.Set("UserID", claim.UserID)
}