Featured image of post golang中JWT权限认证

golang中JWT权限认证

golang中JWT权限认证

包地址

https://github.com/golang-jwt/jwt

一 安装包

1
go get -u github.com/golang-jwt/jwt/v4

二 导入模块

1
import "github.com/golang-jwt/jwt/v4"

三 使用

  1. 使用HMAC 签名算法创建,签名JWT。(HMAC是密钥相关的哈希运算消息认证码 (Hash-based Message Authentication Code)的缩写)

函数NewWithClaims :使用该方法生成token

1
2
func NewWithClaims(method SigningMethod , Claims Claims ) * Token
//第一个参数是签名方法,第二个是声明 返回一个string类型的token

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//签名加密秘钥,bate类型
mySigningKey := []byte("AllYourBase")
// 自定义结构体,在结构体中 embed (嵌入)jwt.RegisteredClaims 结构体
type MyCustomClaims struct {
	Foo string `json:"foo"`
	jwt.RegisteredClaims
}

// Create the claims
claims := MyCustomClaims{
	"bar",
	jwt.RegisteredClaims{
		// A usual scenario is to set the expiration time relative to the current time
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),  // 设置到期时间
		IssuedAt:  jwt.NewNumericDate(time.Now()),                 
		NotBefore: jwt.NewNumericDate(time.Now()),
		Issuer:    "test",                                              // 发布者
		Subject:   "somebody",                                          // 主题
		ID:        "1",
		Audience:  []string{"somebody_else"},
	},
}

// 创建结构体示例忽略不是必须的字段
claims = MyCustomClaims{
	"bar",
	jwt.RegisteredClaims{
		// Also fixed dates can be used for the NumericDate
		ExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),
		Issuer:    "test",
	},
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Printf("%v %v", ss, err)
1
2
3
Output:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.xVuY2FZ_MRXMIEgVQ7J-TFtaucVFRXUzHm9LmV41goM <nil>

2. 解析和验证token

函数ParseWithClaims 

使用自定义声明类型创建令牌的示例。StandardClaim 嵌入在自定义类型中,以便于对标准声明进行编码、解析和验证。

1
2
func ParseWithClaims(tokenString string , Claims , keyFunc Keyfunc , options ... ParserOption ) (* Token , error )
//参数说明:tokenString:token字符串,keyFunc :获取key的函数,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 需要解析的token字符串
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA"
//自定义的结构体
type MyCustomClaims struct {
	Foo string `json:"foo"`
	jwt.RegisteredClaims
}
//token是
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
	return []byte("AllYourBase"), nil    //[]byte("AllYourBase") 时候自定义的加密秘钥
})
//claims是自定义结构体的实例,上面有在生产token时候的传入的信息,如果username,id等
claims, ok := token.Claims.(*MyCustomClaims)
if  ok && token.Valid {
	fmt.Printf("%v %v", claims.Foo, claims.RegisteredClaims.Issuer)
} else {
	fmt.Println(err)
}

在仿小米商城中的使用

在用户登录以后生产token返回到客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 执行登录
func (this *Apiv1Controller) DoLogin() {
	user := models.User{}
	err := this.BindJSON(&user)

	if err != nil {
		this.Data["json"] = map[string]interface{}{
			"status": 201,
			"msg":    "参数错误",
		}
		this.ServeJSON()
		return
	}
	user2 := models.User{}
	models.Db.Where("phone=?", user.Phone).Find(&user2)
	if user2.Id == 0 {
		this.Data["json"] = map[string]interface{}{
			"status": 401,
			"msg":    "账号错误!",
		}
		this.ServeJSON()
		return
	}
	// 账号验证通过,验证密码
	md5Hash := models.Md5(user.Password)
	if user2.Password != md5Hash {
		this.Data["json"] = map[string]interface{}{
			"status": 402,
			"msg":    "密码错误!",
		}
		this.ServeJSON()
		return
	}
	// 密码和账号都正确登录成功

	// 设置加密秘钥
	hmacSampleSecret := []byte("12345aasdadzx")
	//自定义结构体
	type MyClaims struct {
		Uid   int
		Phone string
		jwt.StandardClaims
	}
//token过期时间
	expireTime := time.Now().Add(24 * time.Hour).Unix()
	// 实例化自己定义的结构体
	myClaim := MyClaims{
		user2.Id,
		user2.Phone,
		jwt.StandardClaims{
			ExpiresAt: expireTime,
		},
	}

	// Create a new token object, specifying signing method and the claims
//创建token对象,指定签名方法和结构体
	// you would like it to contain.
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, myToken)

	// Sign and get the complete encoded token as a string using the secret
//使用自定义的秘钥签名生成一个token字符串,返回给客户端
	tokenString, err := token.SignedString(hmacSampleSecret)

	this.Data["json"] = map[string]interface{}{
		"status": 200,
		"msg":    "登录成功!",
		"data": map[string]interface{}{
			"token": "Bearer " + tokenString,
			"phone": user2.Phone,
		},
	}
	this.ServeJSON()

}

对解析token的封装

golang中如何获取客户端传递的token信息

1
2
tokeData := this.Ctx.Input.Header["Autorization"]
//这里获取的还包含 Bearer要分割

tool.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package api

import (
	"strings"

	"github.com/golang-jwt/jwt"
)

// 封装方法验证token
type MyClaims struct {
	Uid   int
	Phone string
	jwt.StandardClaims
}

func ParseToken(tokenData string) (*MyClaims, interface{}) {

	tokenSlice := strings.Split(tokenData, " ")
	var tokenString string
	if len(tokenSlice) > 1 {
		tokenString = tokenSlice[1]
		// token验证秘钥
		hmacSampleSecret := []byte("12345aasdadzx")

		token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
			return hmacSampleSecret, nil
		})
		claims, ok := token.Claims.(*MyClaims)
		if ok && token.Valid {
			return claims, nil
		} else {
			return claims, err
		}
	} else {
		return nil, "非法token"
	}

}

// 封装函数验证用户信息
func ParseResult(this *Apiv1Controller) interface{} {
	// 1 先验证用户信息是否已经登录
	tokenData := this.Ctx.Input.Header("Authorization")
	//1.1判断有没有token信息,没有直接return 出去
	if tokenData == "null" {
		this.Data["json"] = map[string]interface{}{
			"status": 10,
			"msg":    "无效的token",
			"data":   "",
		}
		this.ServeJSON()
		return nil
	}
	// 1.2 有token信息验证token信息是否合法
	myClains, err1 := ParseToken(tokenData)
	if err1 != nil {
		this.Data["json"] = map[string]interface{}{
			"status": 10,
			"msg":    "无效的token",
			"data":   "",
		}
		this.ServeJSON()
		return nil
	}
	return myClains

}