base64フォーマットのjson文字列を読み取る方法をメモ。
今回のシナリオは、AccessTokenなどのクレームがHTTPヘッダーに入ってきたときに読み取るという想定。
TOC
全体像
全体のソースコードは以下の通り。
ginを使ってHTTPサーバを立ち上げ、/ping
に対してX-Verified-Jwt
ヘッダーでJWTトークンをbase64フォーマットで受け取る想定。ちなみに、JWTのペイロード部分のみが送られてくるので完全なJWTトークンではないのでご了承ください。なぜこんなことしてるのか気になる方は下記のページを参照。
main.go
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type AccessToken struct {
Sub string `json:"sub"`
Scopes []string `json:"scope"`
Iss string `json:"iss"`
Groups []string `json:"groups"`
}
func main() {
router := gin.Default()
router.GET("/ping", func(ctx *gin.Context) {
jwtTokenString := ctx.GetHeader("X-Verified-Jwt")
if jwtTokenString == "" {
ctx.JSON(http.StatusUnauthorized, errorResponse(fmt.Errorf("Valid JWT is required")))
return
}
decodedJwtTokenString, err := base64.StdEncoding.DecodeString(jwtTokenString)
if err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
var accessToken AccessToken
err = json.Unmarshal([]byte(decodedJwtTokenString), &accessToken)
if err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "token is valid", "sub": accessToken.Sub})
return
})
router.Run()
}
func errorResponse(err error) gin.H {
return gin.H{"error": err.Error()}
}
base64フォーマットの文字列をデコード
base64のライブラリを使用して、デコード。
decodedString, err = base64.StdEncoding.DecodeString(string)
json文字列をオブジェクトに
jsonの文字列をオブジェクトに格納するためには、jsonライブラリを使用。
事前に取得したいキーバリューを構造体で定義する。
type AccessToken struct {
Sub string `json:"sub"`
Scopes []string `json:"scope"`
Iss string `json:"iss"`
Groups []string `json:"groups"`
}
続いて、先程取得したjsonのバイト列を使ってAccessToken
のオブジェクトにする
var accessToken AccessToken
err = json.Unmarshal([]byte(decodedJwtTokenString), &accessToken)
テスト
main.go
を起動。
go run main.go
別のターミナルでテスト
$ curl -vvv -H "X-Verified-Jwt: eyJleHAiOjM1MzczOTExMDQsImdyb3VwcyI6WyJncm91cDEiLCJncm91cDIiXSwiaWF0IjoxNTM3MzkxMTA0LCJpc3MiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyIsInNjb3BlIjpbInNjb3BlMSIsInNjb3BlMiJdLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9" http://localhost:8080/ping
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> X-Verified-Jwt: eyJleHAiOjM1MzczOTExMDQsImdyb3VwcyI6WyJncm91cDEiLCJncm91cDIiXSwiaWF0IjoxNTM3MzkxMTA0LCJpc3MiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyIsInNjb3BlIjpbInNjb3BlMSIsInNjb3BlMiJdLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Tue, 04 Jan 2022 14:02:11 GMT
< Content-Length: 60
<
* Connection #0 to host localhost left intact
{"message":"token is valid","sub":"testing@secure.istio.io"}
正しいフォーマットだと200 OK
とともにsubの値を返してくれます。
これを利用して、バックエンドサーバでJWTのペイロードをもとに処理が可能となりました。JWTの検証プロセスなどは外出しできたので開発もビジネスロジックに集中することができます。