JWT
JSON Web Token是一项提议的互联网标准,用于创建具有可选签名和/或可选加密的数据,其有效负载保存断言一定数量声明的JSON。令牌使用私有密钥或公钥/私钥进行签名。
结构
- Header(头部):包含描述令牌类型和签名算法的元数据,通常由两部分组成:令牌类型(通常是 “JWT”)和使用的哈希算法(例如,HMAC SHA256 或 RSA)。
- Payload(负载):包含有关声明的信息,有三种类型的声明:
- 注册声明(Reserved Claims):这些是预定义的声明,例如 “iss”(发行者)、“exp”(过期时间)、“sub”(主题)等。
- 公共声明(Public Claims):这些是自定义的声明,可以按照需要添加到令牌中。
- 私有声明(Private Claims):这些是用户自定义的声明,用于共享信息。
- Signature(签名):使用头部中指定的算法和密钥对头部和负载的组合进行签名,以确保令牌的完整性和安全性。
工作原理
- 用户进行身份验证后,服务器生成一个 JWT,将用户的信息(例如用户 ID 或角色)存储在负载中。
- 服务器使用私钥对头部和负载进行签名,生成签名部分,并将 JWT 返回给客户端。
- 客户端将 JWT 存储在本地,通常是在浏览器的 Cookie 或本地存储中。
- 当客户端向服务器发出请求时,它将 JWT 包含在请求的头部中(通常是 “Authorization” 头部)。
- 服务器接收到请求后,验证 JWT 的签名,确保令牌的完整性。它还检查令牌是否在过期时间之前,并验证其他声明。
- 如果 JWT 有效,服务器将根据负载中的信息授予访问权限。这使服务器能够基于令牌中的声明来做出决策,例如,判断用户是否有权限执行特定操作。
在gin框架中使用jwt
项目结构
auth
存放生成token和解析token以及认证中间件
jwt.go
package auth
import (
"errors"
"github.com/dgrijalva/jwt-go"
"time"
)
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
// 定义过期时间 2小时
const TokenExpireDuration = time.Hour * 2
// 定义secret
var MySecret = []byte("mysecret")
// 生成jwt
func GenToken(username string) (string, error) {
c := MyClaims{
username,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
Issuer: "my-project",
},
}
//使用指定的签名方法创建签名对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
//使用指定的secret签名并获得完成的编码后的字符串token
return token.SignedString(MySecret)
}
// 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
//解析token
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return MySecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
jwt_middware.go
package auth
import (
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
// 认证中间件
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()
}
}
routing
存放auth和home两条路由的处理函数
package routing
import (
"github.com/gin-gonic/gin"
"net/http"
"zero/auth"
)
type UserInfo struct {
Username string `json:"username"`
Password string `json:"password"`
}
func AuthHandler(c *gin.Context) {
var user UserInfo
err := c.BindJSON(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
if user.Username == "xzx" && user.Password == "xzx527416" {
//生成token
tokenString, _ := auth.GenToken(user.Username)
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": "success",
"data": gin.H{"token": tokenString},
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2002,
"msg": "鉴权失败",
})
return
}
home.go
package routing
import (
"github.com/gin-gonic/gin"
"net/http"
)
func HomeHandler(c *gin.Context) {
username := c.MustGet("username").(string)
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"data": gin.H{"username": username},
})
}
server
用于启动服务
package main
import (
"github.com/gin-gonic/gin"
"zero/auth"
"zero/routing"
)
func main() {
r := gin.Default()
r.POST("/auth", routing.AuthHandler)
r.GET("/home", auth.JWTAuthMiddleware(), routing.HomeHandler)
r.Run(":9000")
}
启动服务后,使用postman验证
首先使用post方法访问/auth
然后使用get方法访问/home
这里需要携带上面得到的token
具体是在Header添加一条key/value,对应的是Authorization/Bearer token
以上就是jwt的作用和在gin框架中简单使用jwt,感谢观看。