使用 Golang 中的 Echo 框架来处理 JWT
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
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
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
JWT的实现
不解释JWT,请点击以下链接。
我們將在 Echo 中實現 JWT 認證,基本按照 Cookbook 的方式。
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!
我成功了。
现在我们可以实现无状态认证了。