Session【Go】
Sessions、Users
sessionの簡単な例をお見せします。
sessionsテーブルとusersテーブルを用いてユーザー情報を管理するイメージです。
sessionsテーブルのunique idをキーに、usersテーブルの情報を引き出します。
main.goです。
type user struct { // 各ユーザー情報を格納する構造体 UserName, First, Last string } var tpl *template.Template var dbUsers = make(map[string]user) // 模擬usersテーブル var dbSessions = make(map[string]string) // 模擬sessionsテーブル func init() { tpl = template.Must(template.ParseGlob("templates/*")) } func main() { http.HandleFunc("/", index) http.HandleFunc("/bar", bar) http.Handle("/favicon.ico", http.NotFoundHandler()) http.ListenAndServe(":8080", nil) } func index(w http.ResponseWriter, req *http.Request) { c, err := req.Cookie("session") if err != nil { sID, err := uuid.NewV4() // 外部パッケージを用いて、unique idを生成 if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } c = &http.Cookie{ Name: "session", Value: sID.String(), // string化 } http.SetCookie(w, c) // Cookieをセット } var u user if un, ok := dbSessions[c.Value]; ok { // mapからキーで値が取れた場合、okはtrueになる u = dbUsers[un] } if req.Method == http.MethodPost { // formから値を取得 un := req.FormValue("username") f := req.FormValue("firstname") l := req.FormValue("lastname") u := user{ UserName: un, First: f, Last: l, } dbSessions[c.Value] = un dbUsers[un] = u } tpl.ExecuteTemplate(w, "index.gohtml", u) } func bar(w http.ResponseWriter, req *http.Request) { c, err := req.Cookie("session") if err == http.ErrNoCookie { http.Redirect(w, req, "/", http.StatusSeeOther) return } un, ok := dbSessions[c.Value] // CookieのValueをキーに、userの名前を取得 if !ok { http.Redirect(w, req, "/", http.StatusSeeOther) return } u := dbUsers[un] // userの名前をキーに、userの情報を取得 tpl.ExecuteTemplate(w, "bar.gohtml", u) }
templateファイルの紹介は省略しますが、 index.gohtmlにて、username、firstname、lastnameのinput要素を用意します。
Sign-up
先ほどの例を発展させて、Sign-up機能を作ります。
main.go
type user struct { UserName, Password, First, Last string // Passwordを追加 } var tpl *template.Template var dbUsers = make(map[string]user) var dbSessions = make(map[string]string) func init() { tpl = template.Must(template.ParseGlob("templates/*")) } func main() { http.HandleFunc("/", index) http.HandleFunc("/bar", bar) http.HandleFunc("/signup", signup) http.Handle("/favicon.ico", http.NotFoundHandler()) http.ListenAndServe(":8080", nil) } func index(w http.ResponseWriter, req *http.Request) { u := getUser(w, req) tpl.ExecuteTemplate(w, "index.gohtml", u) } func bar(w http.ResponseWriter, req *http.Request) { u := getUser(w, req) if !alreadyLoggedIn(req) { http.Redirect(w, req, "/", http.StatusSeeOther) return } tpl.ExecuteTemplate(w, "bar.gohtml", u) } func signup(w http.ResponseWriter, req *http.Request) { if alreadyLoggedIn(req) { http.Redirect(w, req, "/", http.StatusSeeOther) return } if req.Method == http.MethodPost { un := req.FormValue("username") p := req.FormValue("password") f := req.FormValue("firstname") l := req.FormValue("lastname") if _, ok := dbUsers[un]; ok { http.Error(w, "Username already taken", http.StatusForbidden) return } c := makeSession(w) http.SetCookie(w, c) dbSessions[c.Value] = un // sessionsテーブル(キーはunique id)にusernameを格納 u := user{ UserName: un, Password: p, First: f, Last: l, } dbUsers[un] = u // usersテーブル(キーはusername)にuser情報を格納 http.Redirect(w, req, "/", http.StatusSeeOther) return } tpl.ExecuteTemplate(w, "signup.gohtml", nil) } func getUser(w http.ResponseWriter, req *http.Request) user { c, err := req.Cookie("session") if err != nil { c = makeSession(w) } var u user if un, ok := dbSessions[c.Value]; ok { // mapからキーで値が取れた場合、okはtrueになる u = dbUsers[un] } return u } func alreadyLoggedIn(req *http.Request) bool { c, err := req.Cookie("session") if err != nil { return false } un := dbSessions[c.Value] _, ok := dbUsers[un] // mapからキーで値が取れた場合、okはtrueになる return ok } func makeSession(w http.ResponseWriter) *http.Cookie { sID, err := uuid.NewV4() // 外部パッケージを用いて、unique idを生成 if err != nil { log.Fatal(err) } return &http.Cookie{ Name: "session", Value: sID.String(), } }
templateファイルの紹介は省略しますが、
signup.gohtmlにて、username、firstname、lastname、そしてpasswordのinput要素を用意します。
login
サードパーティのbcryptを使用したloginの方法を下記で紹介しています。
logout
追加箇所を中心に記載します。
logout関数を追加して、http.HandleFuncを追加します。
logoutリンクはtemplate内どこにでも配置することができます。
func main() { ... http.HandleFunc("/logout", logout) ... http.ListenAndServe(":8080", nil) } func logout(w http.ResponseWriter, req *http.Request) { if !alreadyLoggedIn(req) { http.Redirect(w, req, "/", http.StatusSeeOther) // ログイン状態でない場合は、以降の処理が不要 return } c, _ := req.Cookie("session") // エラーハンドリングはalreadyLoggedIn()で行なっているため、不要 delete(dbSessions, c.Value) // sessionsテーブルから削除 c.MaxAge = -1, // 0かマイナス値を指定することで、Cookieを削除できる http.SetCookie(w, c) // 削除を実行 http.Redirect(w, req, "/login", http.StatusSeeOther) }