はじめに
jwtをデコードしユーザー情報を取得する処理を書く機会があったのですが、Goではサードパーティ製のライブラリを使わずとも意外と簡単に実装できたので、コピペするだけで簡単に使用できるサンプルをここで共有したいと思います。
サンプル
func decode(token string) ([]byte, error) {
// jwtはピリオド区切りで3つのパーツに分かれている
// [0]はヘッダー, [1]はペイロード, [2]はシグネチャー
// 今回必要なユーザー情報などはペイロードに含まれているため[1]を取得している
splitToken := strings.Split(token, ".")
if len(splitToken) != 3 {
panic(err)
}
payload := splitToken[1]
// base64は文字数が4の倍数でないとデコードできないため末尾に"="を挿入することでパディングする
paddingLength := ((4 - len(payload)%4) % 4)
padding := strings.Repeat("=", paddingLength)
paddedPayload := strings.Join([]string{payload, padding}, "")
// デコード
decodedPayload, err := base64.StdEncoding.DecodeString(paddedPayload)
if err != nil {
panic(err)
}
return decodedPayload, nil
}
使用例
以下の内容はginのミドルウェアとして使用する場合の例
type sampleClaim struct {
ID string
name string
role string
}
func DecodeJwt() gin.HandlerFunc {
return func(c *gin.Context) {
// リクエストのヘッダーから認証に関する情報を取得
// 内容が空白であればエラーを返す
auth := strings.TrimSpace(c.Request.Header.Get("Authorization"))
if len(auth) <= 0 {
// 適当なエラーレスポンスをここに記述
// 長くなりそうなので省略
}
// 先ほど取得した内容からjwtを抜き出し取得する
token := auth[len("Bearer "):]
decodedToken, err := decode(token)
if err != nil {
// 適当なエラーレスポンスをここに記述
// 長くなりそうなので省略
}
// デコードしたtoken(json)を構造体として定義
var claims sampleClaim
if err := json.Unmarshal(decodedToken, &claims); err != nil {
// 適当なエラーレスポンスをここに記述
// 長くなりそうなので省略
}
// 構造体をcontextに保持させる
c.Set("claims", claims)
c.Next()
}
}
func decode(token string) ([]byte, error) {
// 省略
return []byte{}, nil
}
終わりに
今回はjwtが適切かどうかを検証する処理は記述しませんでしたが、それも標準ライブラリのみで簡単に記述できそうでした。気が向いたらそちらも記事にしようかと思います。
お恥ずかしながらjwtの仕組みを知らずに今まで仕事してきたので、個人的にはとても学びのある経験ができました。
もしコードに誤りや非効率な部分がありましたら遠慮なくコメントしていただけると幸いです。