ginを利用したセッション管理のサンプル
個人的備忘用。
github.com/gin-contrib/sessions
と github.com/gin-contrib/sessions/cookie
を利用しています。
以下、ソースです。
メイン
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
. "SessionInfo"
"routes"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
)
var LoginInfo SessionInfo
func main() {
router := gin.Default()
//テンプレートの設定
router.LoadHTMLGlob("**/view/*.html")
// セッションの設定
store := cookie.NewStore([]byte("secret"))
router.Use(sessions.Sessions("mysession", store))
router.GET("/login", routes.GetLogin)
router.POST("/login", routes.PostLogin)
menu := router.Group("/menu")
menu.Use(sessionCheck())
{
menu.GET("/top", routes.GetMenu)
}
router.POST("/logout", routes.PostLogout)
router.Run(":8080")
}
func sessionCheck() gin.HandlerFunc {
return func(c *gin.Context) {
session := sessions.Default(c)
LoginInfo.UserId = session.Get("UserId")
// セッションがない場合、ログインフォームをだす
if LoginInfo.UserId == nil {
log.Println("ログインしていません")
c.Redirect(http.StatusMovedPermanently, "/login")
c.Abort() // これがないと続けて処理されてしまう
} else {
c.Set("UserId", LoginInfo.UserId) // ユーザidをセット
c.Next()
}
log.Println("ログインチェック終わり")
}
}
説明
1.セッションの宣言
セッションを扱うため、middleware router.Use
を利用する
store := cookie.NewStore([]byte("secret"))
router.Use(sessions.Sessions("mysession", store))
2.セッションチェック
セッション(ログイン状態)をチェックするため、sessionCheck
関数を呼び出す。
全体的にセッションチェックすると、ログイン画面で必ずエラーになってしまうため、グループ化したmiddlewareを利用している。
menu.Use(sessionCheck())
3.ログインチェック
middlewareから呼び出される。
形式は、以下のようにするのはお決まり?
ログファイルの書き出しなどに利用できるかもしれない。
func sessionCheck() gin.HandlerFunc {
return func(c *gin.Context) {
// 処理前にやりたいことを記述(今回は、ログインチェック)
c.Next()
// 処理後にやりたいことを記述
}
##セッション情報
package sessioninfo
type SessionInfo struct {
UserId interface{}
}
説明
セッションで管理したい情報を構造体で簡易する。
今回は、ユーザIDのみ。
コントローラ
package routes
import (
"log"
"net/http"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
func Logout(c *gin.Context) {
//セッションからデータを破棄する
session := sessions.Default(c)
log.Println("セッション取得")
session.Clear()
log.Println("クリア処理")
session.Save()
}
func Login(c *gin.Context, UserId string) {
//セッションにデータを格納する
session := sessions.Default(c)
session.Set("UserId", UserId)
session.Save()
}
説明
ログイン時のセッション情報の書き込みと、ログアウト時のセッション情報をクリア処理
package routes
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func GetLogin(c *gin.Context) {
c.HTML(http.StatusOK, "login", gin.H{
"UserId": "",
"ErrorMessage": "",
})
}
func PostLogin(c *gin.Context) {
log.Println("ログイン処理")
UserId := c.PostForm("userId")
Login(c, UserId) // // 同じパッケージ内のログイン処理
c.Redirect(http.StatusMovedPermanently, "/menu/top")
}
説明
ログイン画面の表示と、ログイン実行時の処理を記述
package routes
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func PostLogout(c *gin.Context) {
log.Println("ログアウト処理")
Logout(c) // 同じパッケージ内のログアウト処理
// ログインフォームに戻す
c.HTML(http.StatusOK, "login", gin.H{
"UserId": "",
"ErrorMessage": "",
})
}
説明
ログアウト実行時の処理を記述
package routes
import (
"net/http"
"github.com/gin-gonic/gin"
)
func GetMenu(c *gin.Context) {
UserId, _ := c.Get("UserId") // ログインユーザの取得
c.HTML(http.StatusOK, "menu", gin.H{"UserId": UserId})
}
説明
メニュー画面の表示の処理を記述
middleware のsessionCheck
で、c.Set("UserId", LoginInfo.UserId)
でセットした値を、UserId, _ := c.Get("UserId")
で取得している。
ビュー
{{define "login"}}
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="UTF-8">
<title>ログイン</title>
</head>
<body>
<form action="/login" method="POST">
{{if ne .ErrorMessage ""}}
<p>{{.ErrorMessage}}</p>
{{end}}
<p>ユーザーID</p>
<p><input type="text" name="userId" value="{{.UserId}}"></p>
<p><input type="submit" value="ログイン"></p>
</form>
</body>
</html>
{{end}}
{{define "menu"}}
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="UTF-8">
<title>メニュー</title>
</head>
<body>
{{ template "logout" . }}
<p>メニュー1</p>
<p>メニュー2</p>
<p>メニュー3</p>
<p>メニュー4</p>
</body>
</html>
{{end}}
{{define "logout"}}
<form action="/logout" method="post">
<p>ユーザーID: {{.UserId}}</p>
<p><input type="submit" value="ログアウト"></p>
</form>
{{end}}