はじめに
JWTによる認証について調べてみた時に軽く動くものを書いてみたときのもの
JWT(JSON Web Tokens)
クライアント・サーバー間で認証する手段の一つ。
ちなみにJWTと書いてジョットと読むらしい。
フォーマット
{base64エンコードしたheader}.{base64エンコードしたclaims}.{署名}
メリット
- 安全
- JWTに署名が含まれているため、改ざんがあってもチェックできるようになっている
- 管理のしやすさ
- URLに含むことができる文字で構成されているからhttpリクエストでの取り扱いがしやすい
などがある。
JWTに関するこれ以上の詳しい説明は今回は省く。
より詳しく知りたければ
https://jwt.io/
あたりを参考にすると良さそう。
今回実装するもの
今回はログインと、ログイン済みの時にレスポンスを取得する機能のみとする
-
POST /login
: ログイン処理 -
GET /restricted/welcome
: 取得したトークンを付与してリクエストした時のみレスポンスが得られる
実装
ディレクトリ構成
.
├── handler
│ └── handler.go
└── server.go
リクエストに対する処理を下記のように実装する
handler/handler.go
package handler
import (
"net/http"
"time"
"github.com/labstack/echo"
"github.com/dgrijalva/jwt-go"
)
func Login() echo.HandlerFunc {
return func(c echo.Context) error {
username := c.FormValue("username")
password := c.FormValue("password")
if username == "test" && password == "test" {
token := jwt.New(jwt.SigningMethodHS256)
// set claims
claims := token.Claims.(jwt.MapClaims)
claims["name"] = "test"
claims["admin"] = true
claims["iat"] = time.Now().Unix()
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
// generate encoded token and send it as response
t, err := token.SignedString([]byte("secret"))
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"token": t,
})
}
return echo.ErrUnauthorized
}
}
func Restricted() echo.HandlerFunc {
return func(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.String(http.StatusOK, "Welcome " + name + "!")
}
}
ルーティングなどは下記のように行う
server.go
package main
import (
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"jwt_sample/handler"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.POST("/login", handler.Login())
r := e.Group("/restricted")
r.Use(middleware.JWT([]byte("secret")))
r.GET("/welcome", handler.Restricted())
e.Logger.Fatal(e.Start(":1323"))
}
実行
まずは起動させてみる
$ go run server.go
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.1.16
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:1323
正常に起動できてることを確認したら/login
でtokenを取得する。
$ curl -X POST -d 'username=test' -d 'password=test' localhost:1323/login
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTk1OTI5MDcxLCJuYW1lIjoidGVzdCJ9.PF-0TU9aYx6CeLEN8q-EHEARkVrFQxBXYsOu173Sdno"}
取得したトークンをヘッダーのAuthorizationにsetし、/restricted
にリクエストするとWelcomeと表示される。
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTk1OTI5MDcxLCJuYW1lIjoidGVzdCJ9.PF-0TU9aYx6CeLEN8q-EHEARkVrFQxBXYsOu173Sdno" localhost:1323/restricted/welcome
Welcome test!
また、試しに誤ったトークンでリクエストしてみると
$ curl -H "Authorization: Bearer eyJhbGciOiJIU" localhost:1323/restricted/welcome
{"message":"invalid or expired jwt"}
としっかり弾かれることが確認できる。