フレームワーク「GIN」を使ってみた(セッション管理)

ginを利用したセッション管理のサンプル
個人的備忘用。

`github.com/gin-contrib/sessions` と `github.com/gin-contrib/sessions/cookie` を利用しています。

以下、ソースです。

## メイン

```go:main.go
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` を利用する

```go:
store := cookie.NewStore([]byte("secret"))
router.Use(sessions.Sessions("mysession", store))
```

2.セッションチェック
セッション（ログイン状態）をチェックするため、`sessionCheck`関数を呼び出す。
全体的にセッションチェックすると、ログイン画面で必ずエラーになってしまうため、グループ化したmiddlewareを利用している。

```go:
menu.Use(sessionCheck())
```
3.ログインチェック

middlewareから呼び出される。
形式は、以下のようにするのはお決まり？

ログファイルの書き出しなどに利用できるかもしれない。

```go
func sessionCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
        // 処理前にやりたいことを記述（今回は、ログインチェック）
	c.Next()
        // 処理後にやりたいことを記述
}
```

##セッション情報

```go:SessionInfo/sessioninfo.go
package sessioninfo

type SessionInfo struct {
	UserId interface{}
}
```

### 説明

セッションで管理したい情報を構造体で簡易する。
今回は、ユーザIDのみ。

## コントローラ

```go:routes/routes.go
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()
}
```

### 説明
ログイン時のセッション情報の書き込みと、ログアウト時のセッション情報をクリア処理


```go:routes/LoginController.go
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")

}
```

### 説明
ログイン画面の表示と、ログイン実行時の処理を記述

```go:routes/LogoutController.go
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": "",
	})
}
```

### 説明
ログアウト実行時の処理を記述


```go:routes/MenuController.go
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")`で取得している。

## ビュー

```html:Login/view/login.html
{{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}}
```

```html:Menu/view/menu.html
{{define "menu"}}
<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <title>メニュー</title>
</head>
<body>

{{ template "logout" . }}
        <p>メニュー１</p>
        <p>メニュー２</p>
        <p>メニュー３</p>
        <p>メニュー４</p>

</body>
</html>
{{end}}
```

```html:Logout/view/logout.html
{{define "logout"}}

<form action="/logout" method="post">
    <p>ユーザーID: {{.UserId}}</p>
    <p><input type="submit" value="ログアウト"></p>
</form>
{{end}}
```