安装jwt 包
"github.com/golang-jwt/jwt/v4"
- 简单的 jwt 生成
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
)
var mySigningKey = []byte("jiangxiaobai.com")
func GenRegisteredClaims() (string, error) {
claims := &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
Issuer: "qimi",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(mySigningKey)
}
func ValidateRegisteredClaims(tokenString string) bool {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
if err != nil {
return false
}
return token.Valid
}
- 自定义生成jwt 主要是需要加一下自定一个字段如 username
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
)
type CustomClaims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
const TokenExpireDuration = time.Hour * 24
var CustomSecret = []byte("爱你没差这一点时差")
func GenToken(username string) (string, error) {
claims := CustomClaims{
username,
jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
Issuer: "my-project",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(CustomSecret)
}
func ParseToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return CustomSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
在gin中如何使用
package controller
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
)
type UserInfo struct {
Username string `"json:username"`
Password string `"json:password"`
}
const TokenExpireDuration = time.Hour * 24
func authLoginHandler(c *gin.Context) {
var user UserInfo
err := c.ShouldBind(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
if user.Username == "jxb" && user.Password == "jxb123" {
tokenString, _ := GenToken(user.Username)
ctx := context.Background()
err = rdb.Set(ctx, user.Username, tokenString, TokenExpireDuration).Err()
if err != nil {
c.JSON(500, gin.H {
"code": 500,
"msg": "redis存token失败"
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"data": gin.H{"token": tokenString},
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2002,
"msg": "鉴权失败",
})
return
}
func Logout(username string) error {
ctx := context.Background()
err := rdb.Del(ctx, username).Err()
if err != nil {
return err
}
return nil
}
鉴权中间件的实现
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusOK, gin.H{
"code": 2003,
"msg": "请求头中auth为空",
})
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{
"code": 2004,
"msg": "请求头中auth格式有误",
})
c.Abort()
return
}
mc, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2005,
"msg": "无效的Token",
})
c.Abort()
return
}
c.Set("username", mc.Username)
c.Next()
}
}
可能会出现用户在操作的时候蓄操token的问题
-
- 怎么蓄token?
-
- 什么时候蓄token?
- 用户调用接口的时候(排除轮询接口,和websokit的接口),需要带token,和username的接口
- 查过期时间是否小于1小时(或者某个特定时长),给蓄时间许多少根据要求来
func JWTAuthMiddlewareWithRefresh() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"msg": "请求头中token不能为空"})
c.Abort()
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
claims, err := ParseToken(tokenString)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"msg": "无效的token"})
c.Abort()
return
}
now := time.Now()
expireTime := claims.ExpiresAt.Time
timeLeft := expireTime.Sub(now)
const refreshThreshold = time.Hour
if timeLeft < refreshThreshold {
newToken, err := GenToken(claims.Username)
if err == nil {
ctx := context.Background()
rdb.Set(ctx, claims.Username, newToken, 24 * time.Hour)
c.Header("X-Refreshed-Token", newToken)
}
}
c.Set("username", claims.Username)
c.Next()
}
}
前端实现蓄token
axios.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
axios.interceptors.response.use(
response => {
const newToken = response.headers['x-refreshed-token'];
if (newToken) {
localStorage.setItem('token', newToken);
console.log('Token已自动续期');
}
return response;
},
error => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);