什么是JWT
jwt全称(JSON WEB TOKEN),是一种后台不做存储的前端身份验证的工具。分为三部分:Header、Claims、Signature
如何创建一个JWT
func NewWithClaims(method SigningMethod, claims Claims) *Token {
return &Token{
Header: map[string]interface{}{
"typ": "JWT",
"alg": method.Alg(),
},
Claims: claims,
Method: method,
}
}
//method是token签发的方法,claims是实现Claims接口的结构体,
type StandardClaims struct {
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`//过期时间
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`//签发人
NotBefore int64 `json:"nbf,omitempty"`//什么时间开始生效
Subject string `json:"sub,omitempty"`
}
生成Token
type MyClaims struct{
Username string`json:"username"`
jwt.StandardClaims
}
func CreateToken()(string,error){
myclaim:=MyClaims{
Username:"zyj",
StandardClaims:jwt.StandardClaims{
NotBefore:time.Now().Unix()-60, //当前时间一分钟之前生效
ExpiresAt:time.Now().Unix()+60*60*2, //两个小时后过期
Issuer:"zyj",
}
}
//生成token
token:=jwt.NewWithClains(jwt.SigningMethodHS256,myclaim)
//token:=jwt.NewWithClains(jwt.SigningMethodHS256,jwt.MapClaims{
// "exp":time.Now().Unix()+5,
// "iss":"zyj",
// "nbf":time.Now().Unix()+5,
// "username":"zyj",
//})
mySigningKey:=[]byte("Come on!")//加密key
return token.SignedString(mySigningKey)
if err!=nil{
fmt.Println("err:",err)
return
}
解析Token
token,err:=jwt.ParseWithClaims(token,claims,func)
token:我们拿到的token字符串
我们用哪个claims结构体发的 这里就传入哪个结构体
func:一个特殊的回调函数 需要固定接受*Token类型指针 返回一个i和err,i为我们的密钥
token是一个jwtToken类型的数据 我们需要的就是其中的Claims
对Claims进行断言 然后进行取用即可
func ParserToken(s string)(*MyClaims,error){
//解析token
token,err:=jwt.ParseWithClaims(s,&MyClaims{},func(token *jwt.Token)(interface{},error){
return mySigningKey,nil
})
return token.Claims.(*MyClaims),err
}
jwt作为gin框架中的中间件使用
package middleware
import (
"errors"
"ginblog/utils"
"ginblog/utils/errmsg"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
type JWT struct {
JwtKey []byte
}
func NewJWT() *JWT {
return &JWT{
[]byte(utils.JwtKey),
}
}
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
// 定义错误
var (
TokenExpired error = errors.New("Token已过期,请重新登录")
TokenNotValidYet error = errors.New("Token无效,请重新登录")
TokenMalformed error = errors.New("Token不正确,请重新登录")
TokenInvalid error = errors.New("这不是一个token,请重新登录")
)
// CreateToken 生成token
func (j *JWT) CreateToken(claims MyClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.JwtKey)
}
// ParserToken 解析token
func (j *JWT) ParserToken(tokenString string) (*MyClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.JwtKey, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenInvalid
}
}
}
if token != nil {
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
return claims, nil
}
return nil, TokenInvalid
}
return nil, TokenInvalid
}
// JwtToken jwt中间件
func JwtToken() gin.HandlerFunc {
return func(c *gin.Context) {
var code int
tokenHeader := c.Request.Header.Get("Authorization")
if tokenHeader == "" {
code = errmsg.ERROR_TOKEN_EXIST
c.JSON(http.StatusOK, gin.H{
"status": code,
"message": errmsg.GetErrMsg(code),
})
c.Abort()
return
}
checkToken := strings.Split(tokenHeader, " ")
if len(checkToken) == 0 {
c.JSON(http.StatusOK, gin.H{
"status": code,
"message": errmsg.GetErrMsg(code),
})
c.Abort()
return
}
if len(checkToken) != 2 || checkToken[0] != "Bearer" {
c.JSON(http.StatusOK, gin.H{
"status": code,
"message": errmsg.GetErrMsg(code),
})
c.Abort()
return
}
j := NewJWT()
// 解析token
claims, err := j.ParserToken(checkToken[1])
if err != nil {
if err == TokenExpired {
c.JSON(http.StatusOK, gin.H{
"status": errmsg.ERROR,
"message": "token授权已过期,请重新登录",
"data": nil,
})
c.Abort()
return
}
// 其他错误
c.JSON(http.StatusOK, gin.H{
"status": errmsg.ERROR,
"message": err.Error(),
"data": nil,
})
c.Abort()
return
}
c.Set("username", claims)
c.Next()
}
}