-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gin-jwt-session with example version 1.0
- Loading branch information
0 parents
commit a531c94
Showing
12 changed files
with
814 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
*.exe | ||
.vendor | ||
vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
The MIT License (MIT) | ||
Copyright (c) 2017, Scott Huang | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software | ||
and associated documentation files (the "Software"), to deal in the Software without restriction, | ||
including without limitation the rights to use, copy, modify, merge, publish, distribute, | ||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software | ||
is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or | ||
substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# gin-jwt-session | ||
|
||
[![GoDoc](https://godoc.org/github.com/ScottHuangZL/gin-jwt-session?status.png)](http://godoc.org/github.com/ScottHuangZL/gin-jwt-session) | ||
|
||
|
||
## Description | ||
|
||
gin-jwt-session is a Go package that provides JWT and Session for Gin framework | ||
It has the following features: | ||
|
||
* Provide JWT tokenstring generate and validate function. | ||
* Provide defautlt secured session | ||
* Provide secured flashes functions | ||
* Simple to use and also allow you manual adjust options if necessary | ||
* Provide sample for you to easily follow up | ||
|
||
## Requirements | ||
|
||
Go 1.6 or above. | ||
|
||
## Installation | ||
|
||
Run the following command to install the package: | ||
|
||
``` | ||
go get github.com/ScottHuangZL/gin-jwt-session | ||
``` | ||
|
||
## Getting Started | ||
|
||
As for example please refer to [gin-jwt-session-example](https://github.com/ScottHuangZL/gin-jwt-session/example). | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"context" | ||
"github.com/ScottHuangZL/gin-jwt-session" | ||
"github.com/ScottHuangZL/gin-jwt-session/example/controllers" | ||
"github.com/ScottHuangZL/gin-jwt-session/example/models" | ||
"github.com/gin-gonic/gin" | ||
"html/template" | ||
"log" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"time" | ||
) | ||
|
||
func main() { | ||
r := gin.Default() | ||
//below are optional setting, you change it or just comment it to let it as default | ||
// session.SecretKey = "You any very secriet key !@#$!@%@" //Any characters | ||
// session.JwtTokenName = "YouCanChangeTokenName" //no blank character | ||
// session.DefaultFlashSessionName = "YouCanChangeTheFlashName" //no blank character | ||
// session.DefaultSessionName = "YouCanChangeTheSessionName" //no blank character | ||
//end of optional setting | ||
session.NewStore() | ||
r.Use(session.ClearMiddleware()) //important to avoid mem leak | ||
setupRouter(r) | ||
|
||
s := &http.Server{ | ||
Addr: ":8080", | ||
Handler: r, | ||
ReadTimeout: 10 * time.Second, | ||
WriteTimeout: 10 * time.Second, | ||
MaxHeaderBytes: 1 << 20, | ||
} | ||
|
||
go func() { | ||
// service connections | ||
if err := s.ListenAndServe(); err != nil { | ||
log.Printf("listen: %s\n", err) | ||
} | ||
}() | ||
// Wait for interrupt signal to gracefully shutdown the server with | ||
// a timeout of 5 seconds. | ||
quit := make(chan os.Signal) | ||
signal.Notify(quit, os.Interrupt) | ||
<-quit | ||
log.Println("Shutdown Server ...") | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer cancel() | ||
if err := s.Shutdown(ctx); err != nil { | ||
log.Fatal("Server Shutdown:", err) | ||
} | ||
log.Println("Server exiting") | ||
} | ||
|
||
func setupRouter(r *gin.Engine) { | ||
r.Delims("{%", "%}") | ||
// Default With the Logger and Recovery middleware already attached | ||
// Set a lower memory limit for multipart forms (default is 32 MiB) | ||
r.MaxMultipartMemory = 8 << 20 // 8 MiB | ||
r.Static("/static", "./static") | ||
r.SetFuncMap(template.FuncMap{ | ||
"formatAsDate": model.FormatAsDate, | ||
}) | ||
r.LoadHTMLGlob("views/**/*") | ||
r.GET("/login", controllers.LoginHandler) | ||
r.GET("/logout", controllers.LoginHandler) //logout also leverage login handler, since it just need clear session | ||
r.POST("/validate-jwt-login", controllers.ValidateJwtLoginHandler) | ||
r.GET("/index.html", controllers.HomeHandler) | ||
r.GET("/index", controllers.HomeHandler) | ||
r.GET("", controllers.HomeHandler) | ||
|
||
r.GET("/some-cookie-example", controllers.SomeCookiesHandler) | ||
|
||
r.GET("/ping", func(c *gin.Context) { | ||
c.JSON(http.StatusOK, gin.H{"ping": "pong"}) | ||
}) | ||
|
||
} | ||
``` | ||
|
||
## Credits | ||
|
||
gin-jwt-session backend leverage | ||
[github.com/gorilla/sessions](https://github.com/gorilla/sessions) | ||
[github.com/dgrijalva/jwt-go](https://github.com/gorilla/sessions) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[run] | ||
init_cmds = [ | ||
["go", "build"], | ||
["./example.exe"] | ||
] # Commands run in start | ||
watch_all = true # Watch all sub-directories | ||
watch_dirs = [ | ||
"$WORKDIR/models", | ||
"$WORKDIR/views", | ||
"$WORKDIR/controllers", | ||
] # Directories to watch | ||
watch_exts = [".go"] # Extensions to watch | ||
ignore = [".git", "node_modules"] # Directories to exclude from watching | ||
ignore_files = [] # Regexps for ignoring specific notifies | ||
build_delay = 1500 # Minimal interval to Trigger build event | ||
interrupt_timout = 15 # Time to wait until force kill | ||
graceful_kill = false # Wait for exit and before directly kill | ||
cmds = [ | ||
["go", "build"], | ||
["./example.exe"] | ||
] # Commands to run | ||
|
||
[sync] | ||
listen_addr = ":5050" | ||
remote_addr = ":5050" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[deps] | ||
github.com/ScottHuangZL/gin-jwt-session = | ||
github.com/gin-gonic/gin = | ||
|
||
[res] | ||
include = views|static | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package controllers | ||
|
||
import ( | ||
"github.com/ScottHuangZL/gin-jwt-session" | ||
"github.com/ScottHuangZL/gin-jwt-session/example/models" | ||
"github.com/gin-gonic/gin" | ||
// "log" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
//LoginHandler for login page , it also can use for logout since it delete all stored session | ||
func LoginHandler(c *gin.Context) { | ||
flashes := session.GetFlashes(c) | ||
session.DeleteAllSession(c) | ||
c.HTML(http.StatusOK, "home/login.html", gin.H{ | ||
"title": "Jwt Login", | ||
"flashes": flashes, | ||
}) | ||
|
||
} | ||
|
||
//HomeHandler is the home handler | ||
//will show home page, also according login/logout action to navigate | ||
func HomeHandler(c *gin.Context) { | ||
// action := strings.ToLower(c.Param("action")) | ||
// path := strings.ToLower(c.Request.URL.Path) | ||
|
||
flashes := session.GetFlashes(c) | ||
username, err := session.ValidateJWTToken(c) | ||
loginFlag := false | ||
if err == nil && username != "" { | ||
loginFlag = true | ||
} | ||
c.HTML(http.StatusOK, "home/index.html", gin.H{ | ||
"title": "Main website", | ||
"now": time.Now(), | ||
"flashes": flashes, | ||
"loginFlag": loginFlag, | ||
"username": username, | ||
}) | ||
} | ||
|
||
//ValidateJwtLoginHandler validate the login and redirect to correct link | ||
func ValidateJwtLoginHandler(c *gin.Context) { | ||
var form model.Login | ||
//try get login info | ||
if err := c.ShouldBind(&form); err != nil { | ||
session.SetFlash(c, "Get login info error: "+err.Error()) | ||
c.Redirect(http.StatusMovedPermanently, "/login") | ||
return | ||
} | ||
//validate login info | ||
if ok := model.ValidateUser(form.Username, form.Password); !ok { | ||
session.SetFlash(c, "Error : username or password") | ||
c.Redirect(http.StatusMovedPermanently, "/login") | ||
return | ||
} | ||
//login info is correct, can generate JWT token and store in clien side now | ||
tokenString, err := session.GenerateJWTToken(form.Username, time.Hour*time.Duration(1)) | ||
if err != nil { | ||
session.SetFlash(c, "Error Generate token string: "+err.Error()) | ||
c.Redirect(http.StatusMovedPermanently, "/login") | ||
return | ||
} | ||
|
||
err = session.SetTokenString(c, tokenString, 60*60) //60 minutes | ||
if err != nil { | ||
session.SetFlash(c, "Error set token string: "+err.Error()) | ||
c.Redirect(http.StatusMovedPermanently, "/login") | ||
return | ||
} | ||
session.SetFlash(c, "success : successful login") | ||
session.SetFlash(c, "username : "+form.Username) | ||
c.Redirect(http.StatusMovedPermanently, "/") | ||
return | ||
} | ||
|
||
//SomeCookiesHandler show cookie example | ||
func SomeCookiesHandler(c *gin.Context) { | ||
session.Set(c, "hello", "world") | ||
sessionMessage, _ := session.GetString(c, "hello") | ||
session.Set(c, "hello", 2017) | ||
message2, _ := session.GetInt(c, "hello") | ||
session.Delete(c, "hello") | ||
readAgain, _ := session.GetString(c, "hello") | ||
c.JSON(http.StatusOK, gin.H{ | ||
"session message": sessionMessage, | ||
"session new message": message2, | ||
"session read again after delete": readAgain, | ||
"status": http.StatusOK}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"github.com/ScottHuangZL/gin-jwt-session" | ||
"github.com/ScottHuangZL/gin-jwt-session/example/controllers" | ||
"github.com/ScottHuangZL/gin-jwt-session/example/models" | ||
"github.com/gin-gonic/gin" | ||
"html/template" | ||
"log" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"time" | ||
) | ||
|
||
func main() { | ||
r := gin.Default() | ||
//below are optional setting, you change it or just comment it to let it as default | ||
// session.SecretKey = "You any very secriet key !@#$!@%@" //Any characters | ||
// session.JwtTokenName = "YouCanChangeTokenName" //no blank character | ||
// session.DefaultFlashSessionName = "YouCanChangeTheFlashName" //no blank character | ||
// session.DefaultSessionName = "YouCanChangeTheSessionName" //no blank character | ||
//end of optional setting | ||
session.NewStore() | ||
r.Use(session.ClearMiddleware()) //important to avoid mem leak | ||
setupRouter(r) | ||
|
||
s := &http.Server{ | ||
Addr: ":8080", | ||
Handler: r, | ||
ReadTimeout: 10 * time.Second, | ||
WriteTimeout: 10 * time.Second, | ||
MaxHeaderBytes: 1 << 20, | ||
} | ||
|
||
go func() { | ||
// service connections | ||
if err := s.ListenAndServe(); err != nil { | ||
log.Printf("listen: %s\n", err) | ||
} | ||
}() | ||
// Wait for interrupt signal to gracefully shutdown the server with | ||
// a timeout of 5 seconds. | ||
quit := make(chan os.Signal) | ||
signal.Notify(quit, os.Interrupt) | ||
<-quit | ||
log.Println("Shutdown Server ...") | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer cancel() | ||
if err := s.Shutdown(ctx); err != nil { | ||
log.Fatal("Server Shutdown:", err) | ||
} | ||
log.Println("Server exiting") | ||
} | ||
|
||
func setupRouter(r *gin.Engine) { | ||
r.Delims("{%", "%}") | ||
// Default With the Logger and Recovery middleware already attached | ||
// Set a lower memory limit for multipart forms (default is 32 MiB) | ||
r.MaxMultipartMemory = 8 << 20 // 8 MiB | ||
r.Static("/static", "./static") | ||
r.SetFuncMap(template.FuncMap{ | ||
"formatAsDate": model.FormatAsDate, | ||
}) | ||
r.LoadHTMLGlob("views/**/*") | ||
r.GET("/login", controllers.LoginHandler) | ||
r.GET("/logout", controllers.LoginHandler) //logout also leverage login handler, since it just need clear session | ||
r.POST("/validate-jwt-login", controllers.ValidateJwtLoginHandler) | ||
r.GET("/index.html", controllers.HomeHandler) | ||
r.GET("/index", controllers.HomeHandler) | ||
r.GET("", controllers.HomeHandler) | ||
|
||
r.GET("/some-cookie-example", controllers.SomeCookiesHandler) | ||
|
||
r.GET("/ping", func(c *gin.Context) { | ||
c.JSON(http.StatusOK, gin.H{"ping": "pong"}) | ||
}) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package model | ||
|
||
//Login is a simple type to contain username and password | ||
type Login struct { | ||
Username string `form:"username" json:"username" binding:"required"` | ||
Password string `form:"password" json:"password" binding:"required"` | ||
} | ||
|
||
//ValidateUser validate the username and password is valid or not | ||
func ValidateUser(username, password string) bool { | ||
if username == "admin" && password == "admin" || | ||
username == "user1" && password == "user1" || | ||
username == "user2" && password == "user2" { | ||
return true | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package model | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
) | ||
|
||
//FormatAsDate return a yyyy-mm-dd date | ||
func FormatAsDate(t time.Time) string { | ||
year, month, day := t.Date() | ||
return fmt.Sprintf("%d-%02d-%02d", year, month, day) | ||
} |
Oops, something went wrong.