こんにちは。KOUKIです。
前回に引き続き、ユーザー認証アプリの実装を行います。本記事は、主にバックエンドで以下の処理について書きます。
1. ログイン機能
2. JWT Tokenの作成
3. Cookieの作成
4. CORSの設定
5. ユーザー認証機能
6. ログアウト機能
作業を始める前に、コンテナを起動しておきましょう。
1 2 |
// コンテナの起動 docker-compose up |
前回
補足:ホットリロードについて
本記事の動作環境では、Mac&reflexツールを使っているため、コンテナを立ち上げなおさなくとも修正内容が自動的に反映される環境(ホットリロード)ですが、Windows環境ではホットリロードが動作しないため、コードの修正があるたびにコンテナを立ち上げなおしてください。
ログイン機能の実装
まずは、ログイン機能を実装しましょう。
DBからユーザー情報を取得
authController.goにLogin関数を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// authController.go package controllers ... // ログイン func Login(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { return err } var user models.User // emailに紐づくユーザーを取得 // &userを指定することでDBから取得したデータを直接格納できる database.DB.Where("email = ?", data["email"]).First(&user) return c.JSON(user) } |
上記のコードでは、リクエストされたメールアドレスと一致するデータをMySQLから取得しています。
これをrouters.goから呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// routes.go package routes import ( "auth-api/controllers" "github.com/gofiber/fiber/v2" ) func Setup(app *fiber.App) { app.Post("/api/register", controllers.Register) app.Post("/api/login", controllers.Login) // 追加 } |
Talend API Testerから動作確認しましょう。
送信先: http://localhost/api/login
形式: POST
1 2 3 4 5 |
// 送信データ { "email": "selfnote@yahoo.co.jp", "password": "a" } |


データを取得することができました。
データが存在しなかった場合
次は、データが存在しなかった場合の処理です。404エラーとエラーメッセージを返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func Login(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { return err } var user models.User database.DB.Where("email = ?", data["email"]).First(&user) if user.ID == 0 { c.Status(404) return c.JSON(fiber.Map{ "message": "User not found", }) } return c.JSON(user) } |
今度は、以下のパラメータでリクエストを送ってみましょう。
1 2 3 4 |
{ "email": "hoge@yahoo.co.jp", "password": "a" } |

パスワードが不一致の場合
リクエストしたパスワードと取得したデータのパスワードが不一致の場合、先ほどと同様に404エラーとエラーメッセージを返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
func Login(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { return err } var user models.User database.DB.Where("email = ?", data["email"]).First(&user) if user.ID == 0 { c.Status(404) return c.JSON(fiber.Map{ "message": "User not found", }) } // パスワードのチェック if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { c.Status(404) return c.JSON(fiber.Map{ "message": "Incorrect password", }) } return c.JSON(user) } |
今度は、以下のパラメータでリクエストを送ってみましょう。
1 2 3 4 |
{ "email": "selfnote@yahoo.co.jp", "password": "hoge" } |

ログインするには、以下のパラメータを送ります。
1 2 3 4 |
{ "email": "selfnote@yahoo.co.jp", "password": "a" } |

OKですね。
JWT Tokenの作成
APIでは、ログイン後にTokenを生成する流れが一般的です。そして、JWTはTokenを作るのに便利なツールです。
Go言語ではjwt-goパッケージを使って、JWT Tokenを作成します。
1 2 |
// パッケージのインストール go get -u github.com/dgrijalva/jwt-go |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// authController.go package controllers import ( "auth-api/database" "auth-api/models" "strconv" "time" "github.com/dgrijalva/jwt-go" "github.com/gofiber/fiber/v2" "golang.org/x/crypto/bcrypt" ) func Register(c *fiber.Ctx) error {...} // ログイン func Login(c *fiber.Ctx) error { ... // パスワードのチェック ... // JWT claims := jwt.StandardClaims{ Issuer: strconv.Itoa(int(user.ID)), // stringに型変換 ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 有効期限 } jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token, err := jwtToken.SignedString([]byte("secret")) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } return c.JSON(fiber.Map{ "jwt": token, }) } |
少し難しいですが、このコードでTokenを作成できます。
次のパラメータで、ログインしてみましょう。
1 2 3 4 |
{ "email": "selfnote@yahoo.co.jp", "password": "a" } |

jwt(Token)が取得できましたね。
Cookieの作成
次は、Cookieを作成します。
ここにjwtをキャッシングすることで、ログイン状態を判定することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// authController.go package controllers ... // ログイン func Login(c *fiber.Ctx) error { ... // JWT ... // Cookie cookie := fiber.Cookie{ Name: "jwt", Value: token, Expires: time.Now().Add(time.Hour * 24), HTTPOnly: true, } c.Cookie(&cookie) return c.JSON(fiber.Map{ "jwt": token, }) } |
fiberのCookieメソッドでCookieにjwt情報を保存しました。
CORSの設定
CORSの設定も追加しておきましょう。
CORSは、異なるオリジン(ここでは、Port番号が異なる)のサイト間通信を可能にします。フロントエンドはポート番号8080、このAPIは80で動くのでCORS設定を入れて通信ができるようにします。
これは、main.goに設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// main.go package main import ( "auth-api/database" "auth-api/routes" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" ) func main() { // GORMセット database.Connect() app := fiber.New() // CORSの設定 app.Use(cors.New(cors.Config{ // https://docs.gofiber.io/api/middleware/cors#config AllowCredentials: true, })) routes.Setup(app) app.Listen(":80") } |
ユーザー認証機能
いよいよユーザー認証機能を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// authController.go package controllers import ( "auth-api/database" "auth-api/models" "strconv" "time" "github.com/dgrijalva/jwt-go" "github.com/gofiber/fiber/v2" "golang.org/x/crypto/bcrypt" ) type Claims struct { jwt.StandardClaims } func User(c *fiber.Ctx) error { // CookieからJWTを取得 cookie := c.Cookies("jwt") // Loginで保存したもの // token取得 token, err := jwt.ParseWithClaims(cookie, &Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte("secret"), nil }) if err != nil || !token.Valid { c.Status(fiber.StatusUnauthorized) return c.JSON(fiber.Map{ "message": "unauthenticated", }) } claims := token.Claims.(*Claims) // User IDを取得 id := claims.Issuer var user models.User database.DB.Where("id = ?", id).First(&user) return c.JSON(user) } ... |
Cookieからjwt情報を取得して、それをtokenに変換してます。難しく感じますが、イディオムだと思って覚えてください。
最終的にtokenからUserIDを取得できるので、そのIDをキーにMySQLを検索して、User情報を取得しています。
1 2 3 4 5 6 7 8 |
function Login() { ... // JWT claims := jwt.StandardClaims{ Issuer: strconv.Itoa(int(user.ID)), <<< ここでID設定 ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), } } |
作成した関数を呼び出せるようにrouters.goも修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// routes.go package routes import ( "auth-api/controllers" "github.com/gofiber/fiber/v2" ) func Setup(app *fiber.App) { app.Post("/api/register", controllers.Register) app.Post("/api/login", controllers.Login) app.Get("/api/user", controllers.User) // 追加 } |
次の条件でリクエストを送ってください。
URL: http://localhost/api/user
形式: GET

Cookieが保存されていない場合は認証できないので、上記のようになります。そのため、以下のリクエストデータでログインをしましょう。
送信先: http://localhost/api/login
形式: POST
1 2 3 4 5 |
// 送信データ { "email": "selfnote@yahoo.co.jp", "password": "a" } |

これでCookieが保存されたはずです。もう一度、「http://localhost/api/user」にリクエストを送ります。

OKですね。
ログアウト機能の実装
続いて、ログアウト機能を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// authController.go package controllers ... func Logout(c *fiber.Ctx) error { cookie := fiber.Cookie{ Name: "jwt", Value: "", // tokenを空にする Expires: time.Now().Add(-time.Hour), // マイナス値を入れて期限切れ HTTPOnly: true, } c.Cookie(&cookie) return c.JSON(fiber.Map{ "message": "success", }) } ... |
やっていることは、空の値をセットしたcookieを作成し、上書きしているだけです。
この関数にアクセスできるようにrouters.goも変更しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// routes.go package routes import ( "auth-api/controllers" "github.com/gofiber/fiber/v2" ) func Setup(app *fiber.App) { app.Post("/api/register", controllers.Register) app.Post("/api/login", controllers.Login) app.Get("/api/user", controllers.User) app.Get("/api/logout", controllers.Logout) // 追加 } |
以下のURLにリクエストを送って、ログアウトできるか確認しましょう。
送信先: http://localhost/api/logout
形式: GET

ログアウトできましたね。
Cookieが消えているか確認するために、「http://localhost/api/user」へリクエストを送信します。

OKですね。
長くなってきたので、ここまでにしましょう。
次回
次回も引き続き、バックエンド開発を行っていきます。
コメントを残す
コメントを投稿するにはログインしてください。