すべての記事の一覧はこちら。
Go言語でWebサイトを作ってみる:目次
http://qiita.com/y_ussie/items/8fcc3077274449478bc9
前回のJSONからのユーザー情報読み込み篇 http://qiita.com/y_ussie/items/8704ce209704bf191e63 の続きになります。
今回は、前回作成したユーザー情報読み込み機能を使用して、ログイン機能を作っていきたいと思います。
やりたいこと
前回は
- ユーザー情報をJSONから読み込んで参照できるようにする
まで作成しましたので、今回は
- ログイン画面からユーザーID/パスワードでログインできるようにする
- ログインユーザーしか見られないページを作ってみる
を実装してみたいと思います。
まずは認証関係の処理をまとめたコードを作成します。
auth.go
package main
import (
"errors"
"./model"
"./session"
"github.com/labstack/echo"
)
// auth.goが返すエラーの定義
var (
ErrorInvalidUserID = errors.New("Invalid UserID")
ErrorInvalidPassword = errors.New("Invalid Password")
ErrorNotLoggedIn = errors.New("Not Logged In")
)
// UserLogin はユーザーログイン時の処理を行います。
func UserLogin(c echo.Context, userID string, password string) error {
users, err := userDA.FindByUserID(userID, model.FindFirst)
if err != nil {
return err
}
user := &users[0]
encodePassword := model.EncodeStringMD5(password)
if user.Password != encodePassword {
return ErrorInvalidPassword
}
sessionID, err := sessionManager.Create()
if err != nil {
return err
}
err = session.WriteCookie(c, sessionID)
if err != nil {
return err
}
sessionStore, err := sessionManager.LoadStore(sessionID)
if err != nil {
return err
}
sessionData := map[string]string{
"user_id": userID,
}
sessionStore.Data = sessionData
err = sessionManager.SaveStore(sessionID, sessionStore)
if err != nil {
return err
}
return nil
}
// UserLogout はユーザーログアウト時の処理を行います。
func UserLogout(c echo.Context) error {
sessionID, err := session.ReadCookie(c)
if err != nil {
return err
}
err = sessionManager.Delete(sessionID)
if err != nil {
return err
}
return nil
}
// CheckUserID は指定されたユーザーIDでログインしているか確認します。
func CheckUserID(c echo.Context, userID string) error {
sessionID, err := session.ReadCookie(c)
if err != nil {
return err
}
sessionStore, err := sessionManager.LoadStore(sessionID)
if err != nil {
return err
}
sessionUserID, ok := sessionStore.Data["user_id"]
if !ok {
return ErrorNotLoggedIn
}
if sessionUserID != userID {
return ErrorInvalidUserID
}
return nil
}
auth.go には、以下の3つの関数を実装しています。
UserLogin()
- パラメータとしてユーザーID, パスワードを受け取り、ユーザー情報と照合して一致するかチェックします。
- チェックの結果、一致した場合には以下の処理を行います。
- セッションデータストアを作成し、セッションIDを取得します。
- セッションIDをCookieに保存します。
- セッションIDをキーにして、セッションデータストアにユーザーIDを保存します。
UserLogout()
- CookieよりセッションIDを取得します。
- セッションIDをキーにして、セッションデータストアの削除を行います。
CheckUserID()
- CookieよりセッションIDを取得します。
- セッションIDをキーにして、セッションデータストアよりユーザーIDを取得します。
- パラメータとして受け取ったユーザーIDと、セッションデータストアより取得したユーザーIDが一致するかチェックします。
セッションデータストアに関する処理は、以前のセッションデータストア篇
http://qiita.com/y_ussie/items/b1db86b0b54ec69bb928
で作成したものです。
これらの関数を使用して、ログイン画面関連のリクエストハンドラを作成します。
e.GET("/login", handleLoginGet)
e.POST("/login", handleLoginPost)
e.POST("/logout", handleLogoutPost)
// GET:/login
func handleLoginGet(c echo.Context) error {
return c.Render(http.StatusOK, "login", nil)
}
// POST:/login
func handleLoginPost(c echo.Context) error {
userID := c.FormValue("userid")
password := c.FormValue("password")
err := UserLogin(c, userID, password)
if err != nil {
c.Echo().Logger.Debugf("User[%s] Login Error. [%s]", userID, err)
msg := "ユーザーIDまたはパスワードが誤っています。"
data := map[string]string{"user_id": userID, "password": "", "msg": msg}
return c.Render(http.StatusOK, "login", data)
}
return c.Redirect(http.StatusTemporaryRedirect, "/users/"+userID)
}
// POST:/logout
func handleLogoutPost(c echo.Context) error {
err := UserLogout(c)
if err != nil {
c.Echo().Logger.Debugf("User Logout Error. [%s]", err)
return c.Render(http.StatusOK, "login", nil)
}
msg := "ログアウトしました。"
data := map[string]string{"user_id": "", "password": "", "msg": msg}
return c.Render(http.StatusOK, "login", data)
}
GET /login
ログイン画面を表示します。「ログイン」押下で POST:/login に遷移します。
POST /login
ログイン処理を行います。ここで UserLogin() を使用しています。
ログインが正常に行われると、/users/<ユーザーID> のページに遷移します。
POST /logout
ログアウト処理を行い、ログイン画面を表示します。
ここまででログインの処理は作成できましたので、最後に /users/<ユーザーID> で遷移する各ユーザーのページに、現在ログイン中かどうかのチェックを追加します。
// GET:/users/:user_id
// POST:/users/:user_id
func handleUsers(c echo.Context) error {
userID := c.Param("user_id")
err := CheckUserID(c, userID)
if err != nil {
c.Echo().Logger.Debugf("User Page[%s] Roll Error. [%s]", userID, err)
msg := "ログインしていません。"
return c.Render(http.StatusOK, "error", msg)
}
users, err := userDA.FindByUserID(c.Param("user_id"), model.FindFirst)
if err != nil {
return c.Render(http.StatusOK, "error", err)
}
user := users[0]
return c.Render(http.StatusOK, "user", user)
}
最初に CheckUserID() でパスパラメータにて指定されたユーザーIDでログインしているかチェックし、ログインしていない場合にはエラーページを表示します。
ログインしている場合にはユーザーのページを表示します。
実行結果
ログイン画面
/login で以下のログイン画面が表示されます。
ユーザー「a-chan」でログインしてみます。
ユーザー画面
/users/a-chan に遷移し、ユーザー「a-chan」の画面が表示されます。
この状態で、アドレスバーに /users/nocchi を入力し、ユーザー「nocchi」のページを表示しようとしてみます。
認証エラー
エラー画面が表示されました。
再度 /users/a-chan を入力し、ユーザー「a-chan」のページを表示してみます。
ユーザー「a-chan」でログイン中なので、こちらは正常に表示されます。
「ログアウト」を押下します。
ログアウト
ログイン画面に戻ります。
今回使用したコードについて
今回使用したコードの一式は以下の GitHub に置いてあります。
https://github.com/yoshinoyaussie/golang-website-sample/tree/chapter-7
次回予定
すべてのユーザーのページを参照できる管理者ユーザーを作成してみます。
また、ユーザー情報の編集・追加・削除ができるようにしてみます。