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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
package main
import (
"log"
"net/http"
"os"
"time"
"github.com/appleboy/gin-jwt/v2"
"github.com/gin-gonic/gin"
)
type login struct {
Username string `form:"username" json:"username" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
var identityKey = "id"
//定义一函数用来处理请求
func helloHandler(c *gin.Context) {
//func ExtractClaims(c *gin.Context) jwt.MapClaims
//ExtractClaims help to extract the JWT claims
//用来将 Context 中的数据解析出来赋值给 claims
//其实是解析出来了 JWT_PAYLOAD 里的内容
/*
func ExtractClaims(c *gin.Context) jwt.MapClaims {
claims, exists := c.Get("JWT_PAYLOAD")
if !exists {
return make(jwt.MapClaims)
}
return claims.(jwt.MapClaims)
}
*/
claims := jwt.ExtractClaims(c)
user, _ := c.Get(identityKey)
c.JSON(200, gin.H{
"userID": claims[identityKey],
"userName": user.(*User).UserName,
"text": "Hello World.",
})
}
// User demo
type User struct {
UserName string
FirstName string
LastName string
}
func main() {
port := os.Getenv("PORT")
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
if port == "" {
port = "8000"
}
// the jwt middleware
authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
//必要项,显示给用户看的域
Realm: "test zone",
//用来进行签名的密钥,就是加盐用的
Key: []byte("secret key"),
//JWT 的有效时间,默认为一小时
Timeout: time.Hour,
//最长的刷新时间,用来给客户端自己刷新 token 用的
MaxRefresh: time.Hour,
//区分身份的key,此key需要每个用户唯一
IdentityKey: identityKey,
//额外需要保存到MapClaims中的值,该函数接收Authenticator返回的interface{},返回jwt.MapClaims,该结构体将保存在context中.IdentityKey的值必须保存在其中
PayloadFunc: func(data interface{}) jwt.MapClaims {
if v, ok := data.(*User); ok {
return jwt.MapClaims{
identityKey: v.UserName,
}
}
return jwt.MapClaims{}
},
//返回某个用户的身份验证信息,有默认函数
// if mw.IdentityHandler == nil {
// mw.IdentityHandler = func(c *gin.Context) interface{} {
// claims := ExtractClaims(c)
// return claims[mw.IdentityKey]
// }
// }
IdentityHandler: func(c *gin.Context) interface{} {
claims := jwt.ExtractClaims(c)
return &User{
UserName: claims[identityKey].(string),
}
},
//必要项, 这个函数用来判断 User 信息是否合法,如果合法就反馈 true,否则就是 false, 认证的逻辑就在这里
Authenticator: func(c *gin.Context) (interface{}, error) {
var loginVals login
if err := c.ShouldBind(&loginVals); err != nil {
return "", jwt.ErrMissingLoginValues
}
userID := loginVals.Username
password := loginVals.Password
if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") {
return &User{
UserName: userID,
LastName: "Bo-Yi",
FirstName: "Wu",
}, nil
}
return nil, jwt.ErrFailedAuthentication
},
//可选项,用来在 Authenticator 认证成功的基础上进一步的检验用户是否有权限,默认为 success
Authorizator: func(data interface{}, c *gin.Context) bool {
if v, ok := data.(*User); ok && v.UserName == "admin" {
return true
}
return false
},
//可以用来定义如果认证不成功的的处理函数,其中s是Authenticator函数中的err的string形式
Unauthorized: func(c *gin.Context, code int, message string) {
c.JSON(code, gin.H{
"code": code,
"message": message,
})
},
//可以用来自定义登录成功后的返回
LoginResponse: func(context *gin.Context, i int, s string, i2 time.Time) {
c.JSON(code, gin.H{
"code" : i,
"message" : message,
"token" : s,
"expire" :i2.Unix(),
})
},
//这个变量定义了从请求中解析 token 的格式
// TokenLookup is a string in the form of "<source>:<name>" that is used
// to extract token from the request.
// Optional. Default value "header:Authorization".
// Possible values:
// - "header:<name>"
// - "query:<name>"
// - "cookie:<name>"
// - "param:<name>"
TokenLookup: "header: Authorization, query: token, cookie: jwt",
// TokenLookup: "query:token",
// TokenLookup: "cookie:token",
//TokenHeadName 是一个头部信息中的字符串
// TokenHeadName is a string in the header. Default value is "Bearer"
TokenHeadName: "Bearer",
// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
//这个指定了提供当前时间的函数,也可以自定义
TimeFunc: time.Now,
})
if err != nil {
log.Fatal("JWT Error:" + err.Error())
}
r.POST("/login", authMiddleware.LoginHandler)
r.NoRoute(authMiddleware.MiddlewareFunc(), func(c *gin.Context) {
claims := jwt.ExtractClaims(c)
log.Printf("NoRoute claims: %#v\n", claims)
c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
})
auth := r.Group("/auth")
// Refresh time can be longer than token timeout
auth.GET("/refresh_token", authMiddleware.RefreshHandler)
auth.Use(authMiddleware.MiddlewareFunc())
{
auth.GET("/hello", helloHandler)
}
if err := http.ListenAndServe(":"+port, r); err != nil {
log.Fatal(err)
}
}
|