为什么使用JWT?
因为在只需要存前端,后端无需存储。

为什么使用双Token?
因为Token只能定时失效,所以时间不能太长,为了用户体验以及考虑服务器压力,所以使用双Token,用一个refreshToken来做刷新,只要未失效,就可以返回新的双Token。

例子:

package main

import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strings"
	"time"
)

// User 用户信息
type User struct {
	Name string `json:"name" form:"name"`
	Pwd  string `json:"pwd" form:"pwd"`
}

type MyClaims struct {
	Name string `json:"name"`
	Pwd  string `json:"pwd"`
	jwt.StandardClaims
}

var accessSecret = []byte("qyrzr")
var refreshSecret = []byte("ar")

// GetToken 获取accessToken和refreshToken
func GetToken(name, pwd string) (string, string) {
	// accessToken 的数据
	aT := MyClaims{
		name,
		pwd,
		jwt.StandardClaims{
			Issuer:    "AR",
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Add(5 * time.Minute).Unix(),
		},
	}
	// refreshToken 的数据
	rT := MyClaims{
		name,
		pwd,
		jwt.StandardClaims{
			Issuer:    "AR",
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Add(time.Hour).Unix(),
		},
	}
	accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, aT)
	refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, rT)
	accessTokenSigned, err := accessToken.SignedString(accessSecret)
	if err != nil {
		fmt.Println("获取Token失败,Secret错误")
		return "", ""
	}
	refreshTokenSigned, err := refreshToken.SignedString(refreshSecret)
	if err != nil {
		fmt.Println("获取Token失败,Secret错误")
		return "", ""
	}
	return accessTokenSigned, refreshTokenSigned
}

func ParseToken(accessTokenString, refreshTokenString string) (*MyClaims, bool, error) {
	fmt.Println("In ParseToken")
	accessToken, err := jwt.ParseWithClaims(accessTokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return accessSecret, nil
	})
	if claims, ok := accessToken.Claims.(*MyClaims); ok && accessToken.Valid {
		return claims, false, nil
	}

	fmt.Println("RefreshToken")
	refreshToken, err := jwt.ParseWithClaims(refreshTokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return refreshSecret, nil
	})
	if err != nil {
		return nil, false, err
	}
	if claims, ok := refreshToken.Claims.(*MyClaims); ok && refreshToken.Valid {
		return claims, true, nil
	}

	return nil, false, errors.New("invalid token")
}

func authHandler(c *gin.Context) {
	fmt.Println("In authHandler")
	var user User
	err := c.ShouldBind(&user)
	if err != nil {
		c.JSON(200, gin.H{
			"code": 2001,
			"msg":  "无效参数",
		})
		fmt.Println(err.Error())
		return
	}
	fmt.Println("user = ", user)
	if !(user.Name == "ar" && user.Pwd == "123456") {
		c.JSON(200, gin.H{
			"code": 2002,
			"msg":  "鉴权失败",
		})
		fmt.Println("User not exist or password error")
		return
	}
	accessTokenString, refreshTokenString := GetToken(user.Name, user.Pwd)
	c.JSON(200, gin.H{
		"code": 200,
		"msg":  "success",
		"data": gin.H{
			"accessToken":  accessTokenString,
			"refreshToken": refreshTokenString,
		},
	})
}

// JWTAuthMiddleware 用鉴权到中间件
func JWTAuthMiddleware() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 默认双Token放在请求头Authorization的Bearer中,并以空格隔开
		authHeader := c.Request.Header.Get("Authorization")
		fmt.Println(c.Request.Header)
		if authHeader == "" {
			c.JSON(200, gin.H{
				"code": 2003,
				"msg":  "请求头中auth为空",
			})
			c.Abort()
			return
		}
		fmt.Println("authHeader = ", authHeader)
		parts := strings.Split(authHeader, " ")
		fmt.Println("len = ", len(parts))
		fmt.Println("parts[0] = ", parts[0])
		if !(len(parts) == 3 && parts[0] == "Bearer") {
			c.JSON(200, gin.H{
				"code": 2004,
				"msg":  "请求头中auth格式有误",
			})
			c.Abort()
			return
		}
		parseToken, isUpd, err := ParseToken(parts[1], parts[2])
		if err != nil {
			c.JSON(200, gin.H{
				"code": 2005,
				"msg":  "无效的Token",
			})
			c.Abort()
			return
		}
		// accessToken 已经失效,需要刷新双Token
		if isUpd {
			parts[1], parts[2] = GetToken(parseToken.Name, parseToken.Pwd)
			// 如果需要刷新双Token时,返回双Token
			c.JSON(200, gin.H{
				"code": 200,
				"msg":  "鉴权成功",
				"data": gin.H{
					"accessToken":  parts[1],
					"refreshToken": parts[2],
				},
			})
		}
		c.Set("username", parseToken.Name)
		c.Next()
	}
}

func main() {
	r := gin.Default()
	// 获取Token
	r.POST("/auth", authHandler)
	// Token访问测试
	r.GET("/home", JWTAuthMiddleware(), func(c *gin.Context) {
		username := c.MustGet("username").(string)
		c.JSON(200, gin.H{
			"code": 2000,
			"msg":  "success",
			"data": gin.H{"username": username},
		})
	})
	r.Run()
}