[Go] Session管理

manager.go

package sessions

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"net/http"
)

type Manager struct {
	database map[string]interface{}
}

var mg Manager

func NewManager() *Manager {
	return &mg
}

func (m *Manager) NewSessionID() string {
	b := make([]byte, 64)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(b)
}

fun (m *Manager) New(r *http.Request, cookieName string)(*Session, error){
	cookie, err := r.Cookie(cookie.Name)
	if err == nil && m.Exists(cookie.Value){
		return nil, errors.New("sessionIDは既に発行されています")
	}

	session := NewSession(m, cookieName)
	session.ID = m.NewSessionID()
	session.request = r

	return session, nil
}

func(m *Manager) Save(r *http.Request, w http.ResponseWriter, session *Session) error {
	m.database[session.ID] = session

	c := &http.Cookie {
		Name: session.Name(),
		Value: session.ID,
		Path: "/",
	}

	http.SetCookie(session.writer, c)
	return nil
}


func(m *Manager) Exists(sessionID string) bool {
	_, r = m.database[sessionID]
	return r
}

func(m *Manager) Get(r *http.Request, cookieName string)(*Session, error){
	cookie, err := r.Cookie(cookieName)
	if err != nil {
		return nil, err
	}

	sessionID := cookie.Value
	buffer, exists := m.database[sessionID]
	if !exists {
		return nil, errors.New("無効なセッションIDです")
	}

	session := buffer.(*Session)
	session.request = r
	return session, nil
}

func(m *Manager) Destroy(sessionID string){
	delete(m.database, sessionID)
}

session.go

package sessions

import(
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/context"
)

const (
	DefaultSessionName = "default-session"
	DefaultCookieName = "default-cookie"
)

type Session struct {
	cookieName string
	ID	string
	manager *Manager
	request *http.request
	writer http.ResponseWriter
	Values map[string]interface{}
}

func NewSession(manager *Manager, cookieName string) *Session {
	return &Session {
		cookieName: cookieName,
		manager: manager,
		Values: map[string]interface{}{},
	}
}

func StartSession(sessionName, cookieName string, manager *Manager) gin.HandlerFunc {
	return func(ctx *gin.Context){
		var session *Session
		var err error
		session, err = manager.Get(ctx.Request, cookieName)
		if err != nil {
			session, err = manager.New(ctx.Request, cookieName)
			if err != nil {
				println(err.Error())
				ctx.Abort()
			}
		}
		session.writer = ctx.Writer
		ctx.Set(sessionName, session)
		defer context.Clear(ctx.Request)
		ctx.Next()
	}
}

func StartDefaultSession(manager *Manager) gin.HandlerFunc {
	return StartSession(DefaultSessionName, DefaultCookieName, manager)
}

func GetSession(c *gin.Context, sessionName string) *Session {
	return c.MustGet(sessionName).(*Session)
}

func GetDefaultSession(c *gin.Context) *Session {
	return GetSession(c, DefaultSessionName)
}

func (s *Session) Save() error {
	return s.manager.Save(s.request, s.writer, s)
}

func (s *Session) Name() string {
	return s.cookieName
}

func (s *Session) Get(key string)(interface{}, bool){
	ret, exists := s.Value[key]
	return ret, exists
}

func(s *Session) Set(key string, val interface{}){
	s.Values[key] = val
}

func(s *Session) Delete(key string){
	delete(s.Values, key)
}

func(s *Session) Terminate(){
	s.manager.Destroy(s.ID)
}

h_Login.go

package req_handler

import (
	"fmt"
	"html/template"
	"net/http"
)

func HandlerLogin(w http.ResponseWrite, req *http.Request){

	tpl := template.Must(template.ParseFiles("templates/login.gtpl"))

	values := map[string]string{}

	if err := tpl.ExecuteTemplate(w, "login.gtpl", values); err != nil {
		fmt.Println(err)
	}
}

fun HandlerExecLogin(w http.ResponseWriter, req *http.Request){

	manager := sessions.NewManager()
	sessions.StartDefaultSession(manager)

	session := manager.Get(req, sessions.DefaultCookieName)
	session.Set("account", req.FormValue("account"))
	session.Save()

	http.Redirect(w, req, "http://192.168.34.10:10443/login-top", 301)
}

h_LoginTop.go

package req_handler

import (
	"fmt"
	"html/template"
	"net/http"
)

func HandlerLoginTop(w http.ResponseWriter, r *http.Request){
	tpl := template.Must(template.ParseFiles("templates/login-top.gtpl"))

	isLoggedIn := ""
	account := ""

	session := manager.Get(req, sessions.DefaultCookieName)
	if session.Values["account"]{
		isLoggedIn = "isLoggedIn"
		account = session.Values["account"]
	}

	values := map[string] string {
		"isLoggedIn": isLoggedIn,
		"account": account,
	}

	if err := tpl.ExecuteTemplate(w, "login-top.gtpl", values); err != nil {
		fmt.Println(err)
	}
}

func HandlerExecLogout(w http.ResponseWriter, req *http.Request){

	session := manager.Get(req, sessions.DefaultCookieName)
	session.Terminate()

	http.Redirect(w, req, "https://192.168.34.10:10443/login", 301)
}

なるほど、ginね
1. usersテーブルでユーザ名とパスワードが一致するかチェックして、一致した場合進めるようにする
2. sessionで管理する

セッションの使い方は覚えていけば良さそう