diff --git a/.gitignore b/.gitignore index d93b5c9..87e07f6 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ _testmain.go .DS_Store .vscode coverage.out +.idea diff --git a/README.md b/README.md index f35dbe5..f5d803e 100644 --- a/README.md +++ b/README.md @@ -65,17 +65,10 @@ type login struct { Password string `form:"password" json:"password" binding:"required"` } -var identityKey = "id" - -func helloHandler(c *gin.Context) { - claims := jwt.ExtractClaims(c) - user, _ := c.Get(identityKey) - c.JSON(200, gin.H{ - "userID": claims[identityKey], - "userName": user.(*User).UserName, - "text": "Hello World.", - }) -} +var ( + identityKey = "id" + port string +) // User demo type User struct { @@ -84,117 +77,149 @@ type User struct { LastName string } -func main() { - port := os.Getenv("PORT") - r := gin.Default() - +func init() { + port = os.Getenv("PORT") if port == "" { port = "8000" } +} +func main() { + engine := gin.Default() // the jwt middleware - authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{ + authMiddleware, err := jwt.New(initParams()) + if err != nil { + log.Fatal("JWT Error:" + err.Error()) + } + + // register middleware + engine.Use(handlerMiddleWare(authMiddleware)) + + // register route + registerRoute(engine, authMiddleware) + + // start http server + if err = http.ListenAndServe(":"+port, engine); err != nil { + log.Fatal(err) + } +} + +func registerRoute(r *gin.Engine, handle *jwt.GinJWTMiddleware) { + r.POST("/login", handle.LoginHandler) + r.NoRoute(handle.MiddlewareFunc(), handleNoRoute()) + + auth := r.Group("/auth", handle.MiddlewareFunc()) + auth.GET("/refresh_token", handle.RefreshHandler) + auth.GET("/hello", helloHandler) +} + +func handlerMiddleWare(authMiddleware *jwt.GinJWTMiddleware) gin.HandlerFunc { + return func(context *gin.Context) { + errInit := authMiddleware.MiddlewareInit() + if errInit != nil { + log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) + } + } +} + +func initParams() *jwt.GinJWTMiddleware { + + return &jwt.GinJWTMiddleware{ Realm: "test zone", Key: []byte("secret key"), Timeout: time.Hour, MaxRefresh: time.Hour, IdentityKey: identityKey, - PayloadFunc: func(data interface{}) jwt.MapClaims { - if v, ok := data.(*User); ok { - return jwt.MapClaims{ - identityKey: v.UserName, - } - } - return jwt.MapClaims{} - }, - IdentityHandler: func(c *gin.Context) interface{} { - claims := jwt.ExtractClaims(c) - return &User{ - UserName: claims[identityKey].(string), - } - }, - 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 - }, - Authorizator: func(data interface{}, c *gin.Context) bool { - if v, ok := data.(*User); ok && v.UserName == "admin" { - return true - } + PayloadFunc: payloadFunc(), - return false - }, - Unauthorized: func(c *gin.Context, code int, message string) { - c.JSON(code, gin.H{ - "code": code, - "message": message, - }) - }, - // TokenLookup is a string in the form of ":" that is used - // to extract token from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "cookie:" - // - "param:" - TokenLookup: "header: Authorization, query: token, cookie: jwt", + IdentityHandler: identityHandler(), + Authenticator: authenticator(), + Authorizator: authorizator(), + Unauthorized: unauthorized(), + TokenLookup: "header: Authorization, query: token, cookie: jwt", // TokenLookup: "query:token", // TokenLookup: "cookie:token", - - // TokenHeadName is a string in the header. Default value is "Bearer" TokenHeadName: "Bearer", + TimeFunc: time.Now, + } +} - // 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, - }) +func payloadFunc() func(data interface{}) jwt.MapClaims { + return func(data interface{}) jwt.MapClaims { + if v, ok := data.(*User); ok { + return jwt.MapClaims{ + identityKey: v.UserName, + } + } + return jwt.MapClaims{} + } +} - if err != nil { - log.Fatal("JWT Error:" + err.Error()) +func identityHandler() func(c *gin.Context) interface{} { + return func(c *gin.Context) interface{} { + claims := jwt.ExtractClaims(c) + return &User{ + UserName: claims[identityKey].(string), + } } +} - // When you use jwt.New(), the function is already automatically called for checking, - // which means you don't need to call it again. - errInit := authMiddleware.MiddlewareInit() +func authenticator() func(c *gin.Context) (interface{}, error) { + return 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 errInit != nil { - log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) + if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") { + return &User{ + UserName: userID, + LastName: "Bo-Yi", + FirstName: "Wu", + }, nil + } + return nil, jwt.ErrFailedAuthentication } +} - r.POST("/login", authMiddleware.LoginHandler) +func authorizator() func(data interface{}, c *gin.Context) bool { + return func(data interface{}, c *gin.Context) bool { + if v, ok := data.(*User); ok && v.UserName == "admin" { + return true + } + return false + } +} - r.NoRoute(authMiddleware.MiddlewareFunc(), func(c *gin.Context) { +func unauthorized() func(c *gin.Context, code int, message string) { + return func(c *gin.Context, code int, message string) { + c.JSON(code, gin.H{ + "code": code, + "message": message, + }) + } +} + +func handleNoRoute() func(c *gin.Context) { + return 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) - } +func helloHandler(c *gin.Context) { + claims := jwt.ExtractClaims(c) + user, _ := c.Get(identityKey) + c.JSON(200, gin.H{ + "userID": claims[identityKey], + "userName": user.(*User).UserName, + "text": "Hello World.", + }) } + ``` ## Demo diff --git a/_example/basic/server.go b/_example/basic/server.go index f3d71c6..0007b69 100644 --- a/_example/basic/server.go +++ b/_example/basic/server.go @@ -6,27 +6,19 @@ import ( "os" "time" - "github.com/gin-gonic/gin" - jwt "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"` + Password string `form:"password" json:"password" binding:"required"`ach } -var identityKey = "id" - -func helloHandler(c *gin.Context) { - claims := jwt.ExtractClaims(c) - user, _ := c.Get(identityKey) - c.JSON(200, gin.H{ - "userID": claims[identityKey], - "userName": user.(*User).UserName, - "text": "Hello World.", - }) -} +var ( + identityKey = "id" + port string +) // User demo type User struct { @@ -35,113 +27,144 @@ type User struct { LastName string } -func main() { - port := os.Getenv("PORT") - r := gin.Default() - +func init() { + port = os.Getenv("PORT") if port == "" { port = "8000" } +} +func main() { + engine := gin.Default() // the jwt middleware - authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{ + authMiddleware, err := jwt.New(initParams()) + if err != nil { + log.Fatal("JWT Error:" + err.Error()) + } + + // register middleware + engine.Use(handlerMiddleWare(authMiddleware)) + + // register route + registerRoute(engine, authMiddleware) + + // start http server + if err = http.ListenAndServe(":"+port, engine); err != nil { + log.Fatal(err) + } +} + +func registerRoute(r *gin.Engine, handle *jwt.GinJWTMiddleware) { + r.POST("/login", handle.LoginHandler) + r.NoRoute(handle.MiddlewareFunc(), handleNoRoute()) + + auth := r.Group("/auth", handle.MiddlewareFunc()) + auth.GET("/refresh_token", handle.RefreshHandler) + auth.GET("/hello", helloHandler) +} + +func handlerMiddleWare(authMiddleware *jwt.GinJWTMiddleware) gin.HandlerFunc { + return func(context *gin.Context) { + errInit := authMiddleware.MiddlewareInit() + if errInit != nil { + log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) + } + } +} + +func initParams() *jwt.GinJWTMiddleware { + return &jwt.GinJWTMiddleware{ Realm: "test zone", Key: []byte("secret key"), Timeout: time.Hour, MaxRefresh: time.Hour, IdentityKey: identityKey, - PayloadFunc: func(data interface{}) jwt.MapClaims { - if v, ok := data.(*User); ok { - return jwt.MapClaims{ - identityKey: v.UserName, - } - } - return jwt.MapClaims{} - }, - IdentityHandler: func(c *gin.Context) interface{} { - claims := jwt.ExtractClaims(c) - return &User{ - UserName: claims[identityKey].(string), - } - }, - 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 - } + PayloadFunc: payloadFunc(), - return nil, jwt.ErrFailedAuthentication - }, - Authorizator: func(data interface{}, c *gin.Context) bool { - if v, ok := data.(*User); ok && v.UserName == "admin" { - return true - } - - return false - }, - Unauthorized: func(c *gin.Context, code int, message string) { - c.JSON(code, gin.H{ - "code": code, - "message": message, - }) - }, - // TokenLookup is a string in the form of ":" that is used - // to extract token from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "cookie:" - // - "param:" - TokenLookup: "header: Authorization, query: token, cookie: jwt", + IdentityHandler: identityHandler(), + Authenticator: authenticator(), + Authorizator: authorizator(), + Unauthorized: unauthorized(), + TokenLookup: "header: Authorization, query: token, cookie: jwt", // TokenLookup: "query:token", // TokenLookup: "cookie:token", - - // TokenHeadName is a string in the header. Default value is "Bearer" TokenHeadName: "Bearer", + TimeFunc: time.Now, + } +} - // 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()) +func payloadFunc() func(data interface{}) jwt.MapClaims { + return func(data interface{}) jwt.MapClaims { + if v, ok := data.(*User); ok { + return jwt.MapClaims{ + identityKey: v.UserName, + } + } + return jwt.MapClaims{} } +} - // When you use jwt.New(), the function is already automatically called for checking, - // which means you don't need to call it again. - errInit := authMiddleware.MiddlewareInit() +func identityHandler() func(c *gin.Context) interface{} { + return func(c *gin.Context) interface{} { + claims := jwt.ExtractClaims(c) + return &User{ + UserName: claims[identityKey].(string), + } + } +} + +func authenticator() func(c *gin.Context) (interface{}, error) { + return 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 + } +} - if errInit != nil { - log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error()) +func authorizator() func(data interface{}, c *gin.Context) bool { + return func(data interface{}, c *gin.Context) bool { + if v, ok := data.(*User); ok && v.UserName == "admin" { + return true + } + return false } +} - r.POST("/login", authMiddleware.LoginHandler) +func unauthorized() func(c *gin.Context, code int, message string) { + return func(c *gin.Context, code int, message string) { + c.JSON(code, gin.H{ + "code": code, + "message": message, + }) + } +} - r.NoRoute(authMiddleware.MiddlewareFunc(), func(c *gin.Context) { +func handleNoRoute() func(c *gin.Context) { + return 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) - } +func helloHandler(c *gin.Context) { + claims := jwt.ExtractClaims(c) + user, _ := c.Get(identityKey) + c.JSON(200, gin.H{ + "userID": claims[identityKey], + "userName": user.(*User).UserName, + "text": "Hello World.", + }) }