├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── session.go └── session_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go_import_path: github.com/go-session/gin-session 4 | go: 5 | - 1.9 6 | before_install: 7 | - go get -t -v ./... 8 | 9 | script: 10 | - go test -race -coverprofile=coverage.txt -covermode=atomic 11 | 12 | after_success: 13 | - bash <(curl -s https://codecov.io/bash) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lyric 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Session middleware for [Gin](https://github.com/gin-gonic/gin) 2 | 3 | [![Build][Build-Status-Image]][Build-Status-Url] [![Codecov][codecov-image]][codecov-url] [![ReportCard][reportcard-image]][reportcard-url] [![GoDoc][godoc-image]][godoc-url] [![License][license-image]][license-url] 4 | 5 | ## Quick Start 6 | 7 | ### Download and install 8 | 9 | ```bash 10 | $ go get -u -v github.com/go-session/gin-session 11 | ``` 12 | 13 | ### Create file `server.go` 14 | 15 | ```go 16 | package main 17 | 18 | import ( 19 | "net/http" 20 | 21 | "github.com/gin-gonic/gin" 22 | "github.com/go-session/gin-session" 23 | ) 24 | 25 | func main() { 26 | app := gin.Default() 27 | 28 | app.Use(ginsession.New()) 29 | 30 | app.GET("/", func(ctx *gin.Context) { 31 | store := ginsession.FromContext(ctx) 32 | store.Set("foo", "bar") 33 | err := store.Save() 34 | if err != nil { 35 | ctx.AbortWithError(500, err) 36 | return 37 | } 38 | 39 | ctx.Redirect(302, "/foo") 40 | }) 41 | 42 | app.GET("/foo", func(ctx *gin.Context) { 43 | store := ginsession.FromContext(ctx) 44 | foo, ok := store.Get("foo") 45 | if !ok { 46 | ctx.AbortWithStatus(404) 47 | return 48 | } 49 | ctx.String(http.StatusOK, "foo:%s", foo) 50 | }) 51 | 52 | app.Run(":8080") 53 | } 54 | ``` 55 | 56 | ### Build and run 57 | 58 | ```bash 59 | $ go build server.go 60 | $ ./server 61 | ``` 62 | 63 | ### Open in your web browser 64 | 65 | 66 | 67 | foo:bar 68 | 69 | 70 | ## MIT License 71 | 72 | Copyright (c) 2018 Lyric 73 | 74 | [Build-Status-Url]: https://travis-ci.org/go-session/gin-session 75 | [Build-Status-Image]: https://travis-ci.org/go-session/gin-session.svg?branch=master 76 | [codecov-url]: https://codecov.io/gh/go-session/gin-session 77 | [codecov-image]: https://codecov.io/gh/go-session/gin-session/branch/master/graph/badge.svg 78 | [reportcard-url]: https://goreportcard.com/report/github.com/go-session/gin-session 79 | [reportcard-image]: https://goreportcard.com/badge/github.com/go-session/gin-session 80 | [godoc-url]: https://godoc.org/github.com/go-session/gin-session 81 | [godoc-image]: https://godoc.org/github.com/go-session/gin-session?status.svg 82 | [license-url]: http://opensource.org/licenses/MIT 83 | [license-image]: https://img.shields.io/npm/l/express.svg -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package ginsession 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/go-session/session" 9 | ) 10 | 11 | type ( 12 | // ErrorHandleFunc error handling function 13 | ErrorHandleFunc func(*gin.Context, error) 14 | // Config defines the config for Session middleware 15 | Config struct { 16 | // error handling when starting the session 17 | ErrorHandleFunc ErrorHandleFunc 18 | // keys stored in the context 19 | StoreKey string 20 | // keys stored in the context 21 | ManageKey string 22 | // defines a function to skip middleware.Returning true skips processing 23 | // the middleware. 24 | Skipper func(*gin.Context) bool 25 | } 26 | ) 27 | 28 | var ( 29 | storeKey string 30 | manageKey string 31 | 32 | // DefaultConfig is the default Recover middleware config. 33 | DefaultConfig = Config{ 34 | ErrorHandleFunc: func(ctx *gin.Context, err error) { 35 | ctx.AbortWithError(500, err) 36 | }, 37 | StoreKey: "github.com/go-session/gin-session/store", 38 | ManageKey: "github.com/go-session/gin-session/manage", 39 | Skipper: func(_ *gin.Context) bool { 40 | return false 41 | }, 42 | } 43 | ) 44 | 45 | // New create a session middleware 46 | func New(opt ...session.Option) gin.HandlerFunc { 47 | return NewWithConfig(DefaultConfig, opt...) 48 | } 49 | 50 | // NewWithConfig create a session middleware 51 | func NewWithConfig(config Config, opt ...session.Option) gin.HandlerFunc { 52 | if config.ErrorHandleFunc == nil { 53 | config.ErrorHandleFunc = DefaultConfig.ErrorHandleFunc 54 | } 55 | 56 | manageKey = config.ManageKey 57 | if manageKey == "" { 58 | manageKey = DefaultConfig.ManageKey 59 | } 60 | 61 | storeKey = config.StoreKey 62 | if storeKey == "" { 63 | storeKey = DefaultConfig.StoreKey 64 | } 65 | 66 | manage := session.NewManager(opt...) 67 | return func(ctx *gin.Context) { 68 | if config.Skipper != nil && config.Skipper(ctx) { 69 | ctx.Next() 70 | return 71 | } 72 | 73 | ctx.Set(manageKey, manage) 74 | store, err := manage.Start(context.Background(), ctx.Writer, ctx.Request) 75 | if err != nil { 76 | config.ErrorHandleFunc(ctx, err) 77 | return 78 | } 79 | ctx.Set(storeKey, store) 80 | ctx.Next() 81 | } 82 | } 83 | 84 | // FromContext Get session storage from context 85 | func FromContext(ctx *gin.Context) session.Store { 86 | v, ok := ctx.Get(storeKey) 87 | if ok { 88 | return v.(session.Store) 89 | } 90 | return nil 91 | } 92 | 93 | // Destroy a session 94 | func Destroy(ctx *gin.Context) error { 95 | v, ok := ctx.Get(manageKey) 96 | if !ok { 97 | return fmt.Errorf("invalid session manager") 98 | } 99 | return v.(*session.Manager).Destroy(nil, ctx.Writer, ctx.Request) 100 | } 101 | 102 | // Refresh a session and return to session storage 103 | func Refresh(ctx *gin.Context) (session.Store, error) { 104 | v, ok := ctx.Get(manageKey) 105 | if !ok { 106 | return nil, fmt.Errorf("invalid session manager") 107 | } 108 | return v.(*session.Manager).Refresh(nil, ctx.Writer, ctx.Request) 109 | } 110 | -------------------------------------------------------------------------------- /session_test.go: -------------------------------------------------------------------------------- 1 | package ginsession 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/go-session/session" 12 | ) 13 | 14 | func TestSession(t *testing.T) { 15 | cookieName := "test_gin_session" 16 | 17 | gin.SetMode(gin.ReleaseMode) 18 | r := gin.New() 19 | r.Use(New( 20 | session.SetCookieName(cookieName), 21 | session.SetSign([]byte("sign")), 22 | )) 23 | 24 | r.Use(func(ctx *gin.Context) { 25 | store := FromContext(ctx) 26 | if ctx.Query("login") == "1" { 27 | foo, ok := store.Get("foo") 28 | fmt.Fprintf(ctx.Writer, "%s:%v", foo, ok) 29 | return 30 | } 31 | 32 | store.Set("foo", "bar") 33 | err := store.Save() 34 | if err != nil { 35 | t.Error(err) 36 | return 37 | } 38 | fmt.Fprint(ctx.Writer, "ok") 39 | }) 40 | 41 | w := httptest.NewRecorder() 42 | req, err := http.NewRequest("GET", "/", nil) 43 | if err != nil { 44 | t.Error(err) 45 | return 46 | } 47 | r.ServeHTTP(w, req) 48 | 49 | res := w.Result() 50 | cookie := res.Cookies()[0] 51 | if cookie.Name != cookieName { 52 | t.Error("Not expected value:", cookie.Name) 53 | return 54 | } 55 | 56 | buf, _ := ioutil.ReadAll(res.Body) 57 | res.Body.Close() 58 | if string(buf) != "ok" { 59 | t.Error("Not expected value:", string(buf)) 60 | return 61 | } 62 | 63 | req, err = http.NewRequest("GET", "/?login=1", nil) 64 | if err != nil { 65 | t.Error(err) 66 | return 67 | } 68 | req.AddCookie(cookie) 69 | 70 | w = httptest.NewRecorder() 71 | r.ServeHTTP(w, req) 72 | 73 | res = w.Result() 74 | buf, _ = ioutil.ReadAll(res.Body) 75 | res.Body.Close() 76 | if string(buf) != "bar:true" { 77 | t.Error("Not expected value:", string(buf)) 78 | return 79 | } 80 | } 81 | --------------------------------------------------------------------------------