[Golang] base64フォーマットのjson文字列を読み取る

base64フォーマットのjson文字列を読み取る方法をメモ。

今回のシナリオは、AccessTokenなどのクレームがHTTPヘッダーに入ってきたときに読み取るという想定。

全体像

全体のソースコードは以下の通り。

ginを使ってHTTPサーバを立ち上げ、/pingに対してX-Verified-JwtヘッダーでJWTトークンをbase64フォーマットで受け取る想定。ちなみに、JWTのペイロード部分のみが送られてくるので完全なJWTトークンではないのでご了承ください。なぜこんなことしてるのか気になる方は下記のページを参照。

JWTの検証をIstioに外出しする
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の検証プロセスなどは外出しできたので開発もビジネスロジックに集中することができます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA