LoginSignup
0
0

JWT認証を作成中にecho.Context.Get()の引数でぬまった話(Golang + Echo)

Posted at

目次

今回の記事の経緯

今回はバックエンド側のGolangを用いたJWT認証のロジックを考えているときにぬまったお話をいたします。

The Clean Architectureに基づいてロジックを下記のように実装していました。

history_controller.go
package controller

import (
	"backend/model"
	"backend/usecase"
	"net/http"
	"strconv"

	"github.com/golang-jwt/jwt/v4"
	"github.com/labstack/echo/v4"
)

type IHistoryController interface {
	GetAllHistory(c echo.Context) error
    // 以下省略
}

type historyController struct {
	hu usecase.IHistoryUsecase
}

func NewHistoryController(hu usecase.IHistoryUsecase) IHistoryController {
	return &historyController{hu}
}

func (hc *historyController) GetAllHistory(c echo.Context) error {
	// jwtTokenに型アサーション
	player := c.Get("player").(*jwt.Token)
	claims := player.Claims.(jwt.MapClaims)
	playerId := claims["player_id"]

	historyRes, err := hc.hu.GetAllHistory(uint(playerId.(float64)))
	if err != nil {
		return c.JSON(http.StatusInternalServerError, err.Error())
	}
	return c.JSON(http.StatusOK, historyRes)
}

// 以下省略~~~~~~~~~~~~~~~~~~~~~~

Model内は、下記のようにしておりました。

history.go
package model

import "time"

type History struct {
	ID uint `json:"id" gorm:"primaryKey"`
	Win uint `json:"win"`
	Lose uint `json:"lose"`
	Money uint `json:"money"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Player Player `json:"player" gorm:"foreignKey:PlayerId; constraint:OnDelete:CASCADE"`
	PlayerId uint `json:"player_id" gorm:"not null"`
}
// 以下省略

ぬまった箇所

上記のコード内で,

player := c.Get("player").(*jwt.Token)

の箇所で私はGet()に関してちゃんと理解していなかったためによりぬまってしまいました。。。。

router.go
package router

import (
	"backend/controller"
	"os"

	echojwt "github.com/labstack/echo-jwt/v4"
	"github.com/labstack/echo/v4"
)

func NewRouter(uc controller.IPlayerController, hc controller.IHistoryController) *echo.Echo {
	e := echo.New()
	e.POST("/signup", uc.SignUp)
	e.POST("/login", uc.Login)
	e.POST("/logout", uc.Logout)

	h := e.Group("/history")
	// middlewareを追加
	h.Use(echojwt.WithConfig(echojwt.Config{
		// jwtを生成した時と同じSECRETkeyを指定
		SigningKey: []byte(os.Getenv("SECRET")),
		// clientから送られてくるjwtTokenがどこに格納されているかを示す
		TokenLookup: "cookie:token",
	}))

	h.GET("", hc.GetAllHistory)
	h.GET("/:historyId", hc.GetPlayerById)
	h.POST("", hc.CreateHistory)
	h.PUT("/:historyId", hc.UpdateHistoryByWinAndLose)
	h.PUT("/:playerId", hc.UpdateHistoryByMoney)
	return e
}

router側で上記のように実装して、Postmanで動作確認してみると、、、、
スクリーンショット 2023-08-22 23.05.22.png
スクリーンショット 2023-08-22 23.06.19.png

Error: socket hang up

が出ちゃいました。。。

当初は何でか分からず、迷走中。。。
そして、debug中にあることに気が付く。
スクリーンショット 2023-08-22 23.10.43.png

そう。player変数にデータが格納されていませんでした。。。

  • そもそもこのGetは何をするものなのか。

これは簡単。連想配列と同じ考え方でOK?なのかもしれない。
"key"に対応する"value"を取得する。

では、どのようにしたら、今回のように(*jwt.Token)に型アサーションして、playerに格納できるようになるのか。

解決策

Get("player")ではなく、Get("user")にする。

そうすると、、

スクリーンショット 2023-08-22 23.25.37.png

Status: 200 OKが出ました。

また、基本ドキュメントを見ても、ここは基本Get("user")にしている。

では、この"user"はどこから出てきたの。。。。

答えはここにありました。

c.Get() is used to retrieve value from the current HTTP request context.

basically the JWT middleware that you are using intercept the HTTP request, validate the JWT token (by reading the HTTP Authorization header and checking token signature) and if everything's fine the middleware store the token information in the echo context for your later use.

The context has a map inside it to store variables, the JWT token is set using the user key, so that you can use the key to retrieve the token from it later on.

要するに、
コンテキストの内部には、変数を保存するためのマップがあり、JWT トークンは"user"キーを使用して設定されるため、後でそのキーを使用してそこからトークンを取得できるみたいです。

結論

jwtTokenに型アサーションする場合のGet()内では、おとなしく、"user"を使いましょう。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0