Sessions

Iris提供快速,功能齐全且易于使用的会话管理器。

Iris sessions使用 kataras/iris/sessions 包.

概述

  1. import "github.com/kataras/iris/sessions"
  2. sess := sessions.Start(http.ResponseWriter, *http.Request)
  3. sess.
  4. ID() string
  5. Get(string) interface{}
  6. HasFlash() bool
  7. GetFlash(string) interface{}
  8. GetFlashString(string) string
  9. GetString(key string) string
  10. GetInt(key string) (int, error)
  11. GetInt64(key string) (int64, error)
  12. GetFloat32(key string) (float32, error)
  13. GetFloat64(key string) (float64, error)
  14. GetBoolean(key string) (bool, error)
  15. GetAll() map[string]interface{}
  16. GetFlashes() map[string]interface{}
  17. VisitAll(cb func(k string, v interface{}))
  18. Set(string, interface{})
  19. SetImmutable(key string, value interface{})
  20. SetFlash(string, interface{})
  21. Delete(string)
  22. Clear()
  23. ClearFlashes()

此示例将说明如何存储会话中的数据。除了Iris之外,您不需要任何第三方库,但如果您想要使用任何东西,请记住Iris与标准库完全兼容。按下即可找到更详细的示例

在此示例中,我们将仅允许经过身份验证的用户在/ secret时段查看我们的秘密消息。要获得访问权限,首先必须访问/登录才能获得有效的会话cookie,并将其登录。此外,他可以访问/注销以撤消对我们的秘密消息的访问权限。

  1. // sessions.go
  2. package main
  3. import (
  4. "github.com/kataras/iris"
  5. "github.com/kataras/iris/sessions"
  6. )
  7. var (
  8. cookieNameForSessionID = "mycookiesessionnameid"
  9. sess = sessions.New(sessions.Config{Cookie: cookieNameForSessionID})
  10. )
  11. func secret(ctx iris.Context) {
  12. //验证用户授权
  13. if auth, _ := sess.Start(ctx).GetBoolean("authenticated"); !auth {
  14. ctx.StatusCode(iris.StatusForbidden)
  15. return
  16. }
  17. //输出消息
  18. ctx.WriteString("The cake is a lie!")
  19. }
  20. func login(ctx iris.Context) {
  21. session := sess.Start(ctx)
  22. // 在里执行验证
  23. // ...
  24. //把验证状态保存为true
  25. session.Set("authenticated", true)
  26. }
  27. func logout(ctx iris.Context) {
  28. session := sess.Start(ctx)
  29. // 撤消用户身份验证
  30. session.Set("authenticated", false)
  31. }
  32. func main() {
  33. app := iris.New()
  34. app.Get("/secret", secret)
  35. app.Get("/login", login)
  36. app.Get("/logout", logout)
  37. app.Run(iris.Addr(":8080"))
  38. }

$ go run sessions.go

$ curl -s http://localhost:8080/secret Forbidden

$ curl -s -I http://localhost:8080/login Set-Cookie: mysessionid=MTQ4NzE5Mz…

$ curl -s —cookie "mysessionid=MTQ4NzE5Mz…" http://localhost:8080/secret The cake is a lie!`

后端存储

有时您需要一个后端存储,即文件存储或redis存储,这将使您的会话数据在服务器重新启动时保持不变。

使用单个调用注册数据库非常容易.UseDatabase(数据库)。

让我们看一个使用快速键值存储螺栓db的简单示例。bolt

  1. package main
  2. import (
  3. "time"
  4. "github.com/kataras/iris"
  5. "github.com/kataras/iris/sessions"
  6. "github.com/kataras/iris/sessions/sessiondb/boltdb"
  7. )
  8. func main() {
  9. db, _ := boltdb.New("./sessions/sessions.db", 0666, "users")
  10. // 使用不同的go协程来同步数据库
  11. db.Async(true)
  12. // 按下control + C / cmd + C时关闭并解锁数据库
  13. iris.RegisterOnInterrupt(func() {
  14. db.Close()
  15. })
  16. sess := sessions.New(sessions.Config{
  17. Cookie: "sessionscookieid",
  18. Expires: 45 * time.Minute, // 0 代表忽略
  19. })
  20. // 重要:
  21. sess.UseDatabase(db)
  22. // 其余的代码保持不变
  23. app := iris.New()
  24. app.Get("/", func(ctx iris.Context) {
  25. ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead")
  26. })
  27. app.Get("/set", func(ctx iris.Context) {
  28. s := sess.Start(ctx)
  29. //设置
  30. s.Set("name", "iris")
  31. //测试如果在这里设置
  32. ctx.Writef("All ok session setted to: %s", s.GetString("name"))
  33. })
  34. app.Get("/set/{key}/{value}", func(ctx iris.Context) {
  35. key, value := ctx.Params().Get("key"), ctx.Params().Get("value")
  36. s := sess.Start(ctx)
  37. // 设置会话值
  38. s.Set(key, value)
  39. // 测试如果在这里设置
  40. ctx.Writef("All ok session setted to: %s", s.GetString(key))
  41. })
  42. app.Get("/get", func(ctx iris.Context) {
  43. // 获取一个特定的键,如字符串,如果没有找到只返回一个空字符串
  44. name := sess.Start(ctx).GetString("name")
  45. ctx.Writef("The name on the /set was: %s", name)
  46. })
  47. app.Get("/get/{key}", func(ctx iris.Context) {
  48. // 获取一个特定的键,如字符串,如果没有找到只返回一个空字符串
  49. name := sess.Start(ctx).GetString(ctx.Params().Get("key"))
  50. ctx.Writef("The name on the /set was: %s", name)
  51. })
  52. app.Get("/delete", func(ctx iris.Context) {
  53. // 删除一个具体的值
  54. sess.Start(ctx).Delete("name")
  55. })
  56. app.Get("/clear", func(ctx iris.Context) {
  57. // 删除所有条目
  58. sess.Start(ctx).Clear()
  59. })
  60. app.Get("/destroy", func(ctx iris.Context) {
  61. //destroy,删除整个会话数据和cookie
  62. sess.Destroy(ctx)
  63. })
  64. app.Get("/update", func(ctx iris.Context) {
  65. //更新过期日期与新日期
  66. sess.ShiftExpiration(ctx)
  67. })
  68. app.Run(iris.Addr(":8080"))
  69. }

创建自定义后端会话存储

您可以通过实现Database接口来创建自己的后端存储。

  1. type Database interface {
  2. Load(sid string) returns struct {
  3. //值包含整个内存存储,此存储
  4. //包含从内存调用更新的当前值,
  5. //会话数据(键和值)。这条路
  6. //数据库可以访问整个会话的数据
  7. // 每次。
  8. Values memstore.Store
  9. //在插入时它包含到期日期时间
  10. //在更新时它包含新的到期日期时间(如果更新或旧的)
  11. //在删除时它将为零
  12. //明确时它将为零
  13. //在销毁时它将为零
  14. Lifetime LifeTime
  15. }
  16. Sync(accepts struct {
  17. //值包含整个内存存储,此存储
  18. //包含从内存调用更新的当前值,
  19. //会话数据(键和值)。这条路
  20. //数据库可以访问整个会话的数据每次。
  21. Values memstore.Store
  22. //在插入时它包含到期日期时间
  23. //在更新时它包含新的到期日期时间(如果更新或旧的)
  24. //在删除时它将为零
  25. //明确时它将为零
  26. //在销毁时它将为零
  27. Lifetime LifeTime
  28. })
  29. }

这就是boltdb会话数据库的样子

  1. package boltdb
  2. import (
  3. "bytes"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "time"
  8. "github.com/boltdb/bolt"
  9. "github.com/kataras/golog"
  10. "github.com/kataras/iris/core/errors"
  11. "github.com/kataras/iris/sessions"
  12. )
  13. // DefaultFileMode用作默认数据库的“fileMode”
  14. //用于创建会话目录路径,打开和写入
  15. //会话boltdb(基于文件)存储。
  16. var (
  17. DefaultFileMode = 0666
  18. )
  19. //数据库BoltDB(基于文件)会话存储。
  20. type Database struct {
  21. table []byte
  22. //服务是下划线BoltDB数据库连接,
  23. //它在`New`或`NewFromDB`初始化。
  24. //可用于获取统计数据。
  25. Service *bolt.DB
  26. async bool
  27. }
  28. var (
  29. //当path或tableName为空时,ErrOptionsMissing在`New`上返回。
  30. ErrOptionsMissing = errors.New("required options are missing")
  31. )
  32. // New创建并返回一个新的BoltDB(基于文件)存储
  33. //基于“路径”的实例。
  34. //路径应包括文件名和目录(也称为fullpath),即sessions / store.db。
  35. //它将删除任何旧的会话文件。
  36. func New(path string, fileMode os.FileMode, bucketName string) (*Database, error) {
  37. if path == "" || bucketName == "" {
  38. return nil, ErrOptionsMissing
  39. }
  40. if fileMode <= 0 {
  41. fileMode = os.FileMode(DefaultFileMode)
  42. }
  43. // create directories if necessary
  44. if err := os.MkdirAll(filepath.Dir(path), fileMode); err != nil {
  45. golog.Errorf("error while trying to create the necessary directories for %s: %v", path, err)
  46. return nil, err
  47. }
  48. service, err := bolt.Open(path, 0600,
  49. &bolt.Options{Timeout: 15 * time.Second},
  50. )
  51. if err != nil {
  52. golog.Errorf("unable to initialize the BoltDB-based session database: %v", err)
  53. return nil, err
  54. }
  55. return NewFromDB(service, bucketName)
  56. }
  57. // NewFromDB与`New`相同,但接受已创建的自定义boltdb连接。
  58. func NewFromDB(service *bolt.DB, bucketName string) (*Database, error) {
  59. if bucketName == "" {
  60. return nil, ErrOptionsMissing
  61. }
  62. bucket := []byte(bucketName)
  63. service.Update(func(tx *bolt.Tx) (err error) {
  64. _, err = tx.CreateBucketIfNotExists(bucket)
  65. return
  66. })
  67. db := &Database{table: bucket, Service: service}
  68. runtime.SetFinalizer(db, closeDB)
  69. return db, db.Cleanup()
  70. }
  71. //清理会删除所有无效(已过期)的会话条目,
  72. //它也会在“新”上自动调用。
  73. func (db *Database) Cleanup() error {
  74. err := db.Service.Update(func(tx *bolt.Tx) error {
  75. b := db.getBucket(tx)
  76. c := b.Cursor()
  77. for k, v := c.First(); k != nil; k, v = c.Next() {
  78. if len(k) == 0 { // empty key, continue to the next pair
  79. continue
  80. }
  81. storeDB, err := sessions.DecodeRemoteStore(v)
  82. if err != nil {
  83. continue
  84. }
  85. if storeDB.Lifetime.HasExpired() {
  86. if err := c.Delete(); err != nil {
  87. golog.Warnf("troubles when cleanup a session remote store from BoltDB: %v", err)
  88. }
  89. }
  90. }
  91. return nil
  92. })
  93. return err
  94. }
  95. // Async如果为true,那么它将使用不同的
  96. //go协程来更新BoltDB(基于文件的)存储。
  97. func (db *Database) Async(useGoRoutines bool) *Database {
  98. db.async = useGoRoutines
  99. return db
  100. }
  101. //加载来自BoltDB(基于文件)会话存储的会话。
  102. func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
  103. bsid := []byte(sid)
  104. err := db.Service.View(func(tx *bolt.Tx) (err error) {
  105. // db.getSessBucket(tx, sid)
  106. b := db.getBucket(tx)
  107. c := b.Cursor()
  108. for k, v := c.First(); k != nil; k, v = c.Next() {
  109. if len(k) == 0 { // empty key, continue to the next pair
  110. continue
  111. }
  112. if bytes.Equal(k, bsid) { // session id should be the name of the key-value pair
  113. storeDB, err = sessions.DecodeRemoteStore(v) // decode the whole value, as a remote store
  114. break
  115. }
  116. }
  117. return
  118. })
  119. if err != nil {
  120. golog.Errorf("error while trying to load from the remote store: %v", err)
  121. }
  122. return
  123. }
  124. // Sync同步数据库与会话(内存)存储。
  125. func (db *Database) Sync(p sessions.SyncPayload) {
  126. if db.async {
  127. go db.sync(p)
  128. } else {
  129. db.sync(p)
  130. }
  131. }
  132. func (db *Database) sync(p sessions.SyncPayload) {
  133. bsid := []byte(p.SessionID)
  134. if p.Action == sessions.ActionDestroy {
  135. if err := db.destroy(bsid); err != nil {
  136. golog.Errorf("error while destroying a session(%s) from boltdb: %v",
  137. p.SessionID, err)
  138. }
  139. return
  140. }
  141. s, err := p.Store.Serialize()
  142. if err != nil {
  143. golog.Errorf("error while serializing the remote store: %v", err)
  144. }
  145. err = db.Service.Update(func(tx *bolt.Tx) error {
  146. return db.getBucket(tx).Put(bsid, s)
  147. })
  148. if err != nil {
  149. golog.Errorf("error while writing the session bucket: %v", err)
  150. }
  151. }
  152. func (db *Database) destroy(bsid []byte) error {
  153. return db.Service.Update(func(tx *bolt.Tx) error {
  154. return db.getBucket(tx).Delete(bsid)
  155. })
  156. }
  157. func (db *Database) getBucket(tx *bolt.Tx) *bolt.Bucket {
  158. return tx.Bucket(db.table)
  159. }
  160. // Len报告存储到此BoltDB表的会话数。
  161. func (db *Database) Len() (num int) {
  162. db.Service.View(func(tx *bolt.Tx) error {
  163. // Assume bucket exists and has keys
  164. b := db.getBucket(tx)
  165. if b == nil {
  166. return nil
  167. }
  168. b.ForEach(func([]byte, []byte) error {
  169. num++
  170. return nil
  171. })
  172. return nil
  173. })
  174. return
  175. }
  176. // 关闭BoltDB连接。FUNC
  177. func (db *Database) Close() error {
  178. return closeDB(db)
  179. }
  180. func closeDB(db *Database) error {
  181. err := db.Service.Close()
  182. if err != nil {
  183. golog.Warnf("closing the BoltDB connection: %v", err)
  184. }
  185. return err
  186. }