1. 前言
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
2. gin集成Jwt
gin 可以自定义中间件,所以集成jwt可以以中间件的方式引入,jwt在golang上已有好几个现成的开源库,我用的是jwt-go,具体使用可以查看GitHub、或 https://pkg.go.dev/github.com/dgrijalva/jwt-go/v4中查看相关使用。
3. 使用
实例场景:用户登录成功后,获取到 token,访问后续的接口获取数据时必须在请求头中携带token,验证token成功后方可正常访问;
3.1 结构如下
3.2 相关文件说明
3.2.1 main.go文件
启动类gin的整合处理
package main
import (
"gin_jwt/router"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
router.Router(r)
r.Run()
}
3.2.2 jwtutil.go文件
token的生成、解析及刷新相关操作的工具类
package utils
import (
"errors"
"fmt"
"github.com/dgrijalva/jwt-go/v4"
"time"
)
type Users struct {
Username string `json:"username"`
Password string `json:"password"`
}
type CustomClaims struct {
Users
jwt.StandardClaims
}
var MySecret = []byte("密钥")
func GenToken(user Users) (string, error) {
claim := CustomClaims{
user,
jwt.StandardClaims{
ExpiresAt: jwt.At(time.Now().Add(time.Minute * 5)),
Issuer: "xx",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
return token.SignedString(MySecret)
}
func ParseToken(tokenStr string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return MySecret, nil
})
if err != nil {
fmt.Println(" token parse err:", err)
return nil, err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
func RefreshToken(tokenStr string) (string, error) {
jwt.TimeFunc = func() time.Time {
return time.Unix(0, 0)
}
token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return MySecret, nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
jwt.TimeFunc = time.Now
claims.StandardClaims.ExpiresAt = jwt.At(time.Now().Add(time.Minute * 10))
return GenToken(claims.Users)
}
return "", errors.New("Cloudn't handle this token")
}
3.2.3 middleware.go文件
jwt中间件,用于token的验证解析
package middelware
import (
"errors"
"gin_jwt/utils"
"github.com/gin-gonic/gin"
"log"
"net/http"
"strings"
)
func JWTAuth() gin.HandlerFunc {
return func(ctx *gin.Context) {
authHeader := ctx.Request.Header.Get("Authorization")
if authHeader == "" {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "无权限访问,请求未携带token",
})
ctx.Abort()
return
}
log.Print("token:", authHeader)
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "请求头中auth格式有误",
})
ctx.Abort()
return
}
claims ,err := utils.ParseToken(parts[1])
if err != nil {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "无效的Token",
})
ctx.Abort()
return
}
ctx.Set("claims", claims)
ctx.Next()
}
}
func CheckUserInfo(claims *utils.CustomClaims) error {
username := claims.Username
password := claims.Password
if username == "admin" && password == "123456"{
return nil
}
return errors.New("用户名或密码错误")
}
3.2.4 router.go文件
路由文件,统一管理路由。其中注意中间件必须在登录后面,登录不需要jwt验证。
package router
import (
"gin_jwt/controller"
"gin_jwt/middelware"
"github.com/gin-gonic/gin"
)
func Router(r *gin.Engine) {
r.GET("/login", controller.LoginController)
r.Use(middelware.JWTAuth())
r.GET("/list", controller.UserListController)
}
3.2.5 jwtTest.go文件
接口测试,大概流程如下(均为get请求):
- 登录获取token,发起请求 :http://localhost:8080/login?username=admin&password=123456
- 使用postman或其他接口测试工具,发起请求:http://localhost:8080/list,注意设置请求头:Authorization : Bearer token,设置如下:
package controller
import (
"gin_jwt/utils"
"github.com/gin-gonic/gin"
"net/http"
)
func LoginController(ctx *gin.Context) {
username := ctx.Query("username")
password := ctx.Query("password")
if !(username == "admin" && password == "123456") {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": "用户名或密码错误",
})
return
}
user := utils.Users{
Username: "admin",
Password: "123456",
}
token, err := utils.GenToken(user)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"msg": err,
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "成功",
"data": gin.H{"token": token},
})
}
func UserListController(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "成功",
"data": "[]",
})
}
|