JSON Web Token (JWT)はマイクロサービスの認証、認可に使える署名付きJSON。ユーザは内容を改ざんできないため、サーバ側に認証ステータスを保持しなくて良いし、検証時に認証サーバにアクセス不要。なので、スケーラブルな認証サービスをJSONのシンプルさを損なわず実装できる。GoogleはOAuth2のトークンに採用しているし、広く採用されている標準と言える。
Echoフレームワークでは、JWT Middlewareを使用可能。公式のサンプル
公式のサンプルは共有秘密鍵を使った署名であるHMACを使っているが、実際に運用する場合には秘密鍵を個別のマイクロサービスに複製しなくて良い公開鍵暗号方式の署名の方が楽なので、やりかたを調べてみた。
署名方法をRSAにして、JWTWithConfigをミドルウェアに渡してあげるだけ。楽チンですね :)
package main
import (
"io/ioutil"
"net/http"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func login(c echo.Context) error {
username := c.FormValue("username")
password := c.FormValue("password")
if username == "jon" && password == "shhh!" {
// Create token
token := jwt.New(jwt.SigningMethodRS512)
// Set claims
claims := token.Claims.(jwt.MapClaims)
claims["name"] = "Jon Snow"
claims["admin"] = true
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
keyData, _ := ioutil.ReadFile("sample_key")
key, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)
// Generate encoded token and send it as response.
t, err := token.SignedString(key)
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{
"token": t,
})
}
return echo.ErrUnauthorized
}
func accessible(c echo.Context) error {
return c.String(http.StatusOK, "Accessible")
}
func restricted(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+"!")
}
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Login route
e.POST("/login", login)
// Unauthenticated route
e.GET("/", accessible)
// Restricted group
r := e.Group("/restricted")
keyData, _ := ioutil.ReadFile("sample_key.pub")
key, _ := jwt.ParseRSAPublicKeyFromPEM(keyData)
r.Use(middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: key,
SigningMethod: "RS512",
}))
r.GET("", restricted)
e.Logger.Fatal(e.Start(":1323"))
}