LoginSignup
19
9

More than 3 years have passed since last update.

GolangのEchoでJWT

Posted at

Echoの準備

DockerでGolangコンテナを使います。

FROM golang:1.14.2-alpine3.11

WORKDIR /go/src

COPY ./src /go/src

ENV GOPATH ''

RUN apk update && apk add --no-cache git
docker-compose.yml
version: '3'
services:
  goecho:
    image: goecho
    build: .
    ports:
      - 8080:8080
    volumes:
      - ./src:/go/src
    tty: true
$ docker-compose up -d --build
$ docker-compose exec goecho go mod init src
$ docker-compose exec goecho go get github.com/labstack/echo/v4
src/main.go
package main

import (
  "net/http"
  "github.com/labstack/echo/v4"
  "github.com/labstack/echo/v4/middleware"
)

func main() {
  // Echo instance
  e := echo.New()

  // Middleware
  e.Use(middleware.Logger())
  e.Use(middleware.Recover())

  // Routes
  e.GET("/", hello)

  // Start server
  e.Logger.Fatal(e.Start(":8080"))
}

// Handler
func hello(c echo.Context) error {
  return c.String(http.StatusOK, "Hello, World!")
}
$ docker-compose exec goecho go run main.go

スクリーンショット 2020-04-22 16.21.59.png

JWTの実装

JWTの説明は省略します。知りたい方は以下のリンク先を。

EchoでJWT認証を実装します。まあCookbook通りですが。。

src/main.go
package main

import (
  "net/http"
  "time"
  "github.com/dgrijalva/jwt-go"
  "github.com/labstack/echo/v4"
  "github.com/labstack/echo/v4/middleware"
)

func login(c echo.Context) error {
  username := c.FormValue("username")
  password := c.FormValue("password")

  // とりあえずのパスワード認証
  if username != "taro" || password != "shhh!" {
    return echo.ErrUnauthorized
  }

  // トークン作成
  token := jwt.New(jwt.SigningMethodHS256)

  claims := token.Claims.(jwt.MapClaims)
  claims["name"] = "Taro"
  claims["admin"] = true
  claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

  t, err := token.SignedString([]byte("secret"))
  if err != nil {
    return err
  }

  return c.JSON(http.StatusOK, map[string]string{
    "token": t,
  })
}

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()

  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")
  r.Use(middleware.JWT([]byte("secret")))
  r.GET("", restricted)

  e.Logger.Fatal(e.Start(":8080"))
}

JWT認証の必要がないURLはアクセスできます。

$ curl localhost:8080
Accessiblesrc

JWT認証が必要なURLはトークンが必要です。

$ curl localhost:8080/restricted
{"message":"missing or malformed jwt"}

トークンを取得します。

$ curl -X POST -d 'username=taro' -d 'password=hoge' localhost:8080/login
{"message":"Unauthorized"}
$ curl -X POST -d 'username=taro' -d 'password=shhh!' localhost:8080/login
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTg3NjI4NjI0LCJuYW1lIjoiVGFybyJ9.5mqtxA0XiNcslwC22n7gNL97KPl6zYdt6AhpF9PkIKk"}

取得したトークンで認証できるか確認します。

$ curl localhost:8080/restricted  -H "Authorization: Bearer hoge"
{"message":"invalid or expired jwt"}
$ curl localhost:8080/restricted  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNTg3NjI4NjI0LCJuYW1lIjoiVGFybyJ9.5mqtxA0XiNcslwC22n7gNL97KPl6zYdt6AhpF9PkIKk"
Welcome Taro!

成功しました。
これでステートレスな認証が可能ですね:beer:

19
9
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
19
9