token 我的理解是一种凭证,客户端请求时携带此凭证才能有效访问需要验证凭证的服务端接口,而且token可以加密携带客户端的一些信息,比如基本的信息是有效期,生效日期,可以看作是令牌。加密后是一串字符串
基于JWT的Token认证机制实现
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
JWT的组成
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
例子
jwt.go 负责token生成,验证
package jwt
import (
"github.com/gin-gonic/gin"
"github.com/dgrijalva/jwt-go"
"errors"
"time"
"net/http"
"log"
)
// 中间件,检查token
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.Request.Header.Get("token")
if token == ""{
c.JSON(http.StatusOK,gin.H{
"status":-1,
"msg":"请求未携带token,无权限访问",
})
c.Set("isPass", false)
return
}
log.Print("get token: ",token)
j := NewJWT()
// parseToken
claims, err := j.ParseToken(token)
if err != nil {
if err == TokenExpired {
c.JSON(http.StatusOK,gin.H{
"status":-1,
"msg":"授权已过期",
})
c.Set("isPass", false)
return
}
c.JSON(http.StatusOK, gin.H{
"status": -1,
"msg": err.Error(),
})
c.Set("isPass", false)
return
}
c.Set("isPass", true)
c.Set("claims",claims)
}
}
// 签名
type JWT struct {
SigningKey []byte
}
var (
TokenExpired error = errors.New("Token is expired")
TokenNotValidYet error = errors.New("Token not active yet")
TokenMalformed error = errors.New("That's not even a token")
TokenInvalid error = errors.New("Couldn't handle this token:")
SignKey string = "newtrekWang"
)
// 载荷
type CustomClaims struct {
ID string `json:"userId"`
Name string `json:"name"`
Phone string `json:"phone"`
jwt.StandardClaims
}
func NewJWT() *JWT {
return &JWT{
[]byte(GetSignKey()),
}
}
func GetSignKey() string {
return SignKey
}
func SetSignKey(key string) string {
SignKey = key
return SignKey
}
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.SigningKey)
}
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, 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 claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, TokenInvalid
}
func (j *JWT) RefreshToken(tokenString string) (string, error) {
jwt.TimeFunc = func() time.Time {
return time.Unix(0, 0)
}
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
jwt.TimeFunc = time.Now
claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
return j.CreateToken(*claims)
}
return "", TokenInvalid
}
api.go 路由处理
package api
import (
"github.com/gin-gonic/gin"
"net/http"
myjwt "ColdChainServer/middleware/jwt"
jwtgo "github.com/dgrijalva/jwt-go"
"time"
"log"
"ColdChainServer/module"
)
func Test(c *gin.Context){
c.JSON(http.StatusOK,gin.H{
"message":"hello",
})
}
func GetDataByTime(c *gin.Context) {
isPass := c.GetBool("isPass")
if !isPass {
return
}
claims := c.MustGet("claims").(*myjwt.CustomClaims)
if claims != nil {
c.JSON(http.StatusOK, gin.H{
"status": 0,
"msg": "token有效",
"data": claims,
})
}
}
type LoginResult struct{
Token string `json:"token"`
module.User
}
// 登录
func Login(c *gin.Context) {
var loginReq module.LoginReq
if c.BindJSON(&loginReq) == nil{
isPass,user,err := module.LoginCheck(loginReq)
if isPass {
generateToken(c,user)
}else {
c.JSON(http.StatusOK,gin.H{
"status":-1,
"msg":"验证失败"+err.Error(),
})
return
}
}else{
c.JSON(http.StatusOK,gin.H{
"status":-1,
"msg":"json 解析失败",
})
return
}
}
// 生成令牌
func generateToken(c *gin.Context, user module.User) {
j := &myjwt.JWT{
[]byte("newtrekWang"),
}
claims := myjwt.CustomClaims{
user.Id,
user.Name,
user.Phone,
jwtgo.StandardClaims{
NotBefore: int64(time.Now().Unix() - 1000),// 签名生效时间
ExpiresAt: int64(time.Now().Unix() + 3600),// 过期时间 一小时
Issuer: "newtrekWang",//签名的发行者
},
}
token, err := j.CreateToken(claims)
if err != nil {
c.JSON(http.StatusOK,gin.H{
"status":-1,
"msg":err.Error(),
})
return
}
log.Println(token)
data := LoginResult{
User:user,
Token:token,
}
c.JSON(http.StatusOK,gin.H{
"status":0,
"msg":"登录成功!",
"data":data,
})
return
}
main.go 路由分发
package main
import (
"github.com/gin-gonic/gin"
"ColdChainServer/api"
"ColdChainServer/middleware/jwt"
)
func main() {
r := gin.Default()
r.GET("/",api.Test)
r.POST("/login",api.Login)
r.POST("/register",api.Register)
r.POST("/editUser",api.UpdateUser)
taR := r.Group("/data")
taR.Use(jwt.JWTAuth())
{
taR.GET("/dataByTime",api.GetDataByTime)
}
r.Run(":8080")
}
验证功能
登录
请求需要token的接口
携带token
未携带token
无效token