JWTの紹介
JWT (JSON Web Token) は、ウェブ上での認証のための簡潔で自己完結型の方法です。ヘッダー、ペイロード、署名の3つの部分から構成されています。ヘッダーにはトークンのタイプや暗号化アルゴリズム情報が含まれ、ペイロードには通過すべきデータが含まれます(通常は権限情報やユーザー識別情報など)、そして署名はトークンの整合性と有効性を検証するために使用されます。
JWTは主に2つのシナリオで使用されます:
- 認証:ユーザーがログインすると、その後の各リクエストにはJWTが含まれ、ユーザーは許可されたルート、サービス、リソースにアクセスできます。
- 情報交換:JWTは情報を安全にやり取りするための良い方法であり、例えば公開/秘密鍵ペアを使用してデジタル的に署名されるためです。
JWTデータ形式
JWTは情報を交換するためのコンパクトでURLセーフな方法です。JWTは実際には Header.Payload.Signature
の3つの部分で構成され、それぞれがドット (.) で区切られています。次に、これら3つの部分のデータ形式について詳しく説明します。
1. ヘッダー
ヘッダーには通常2つの部分が含まれます:トークンのタイプ(通常は JWT
)と使用される署名または暗号化アルゴリズム(例: HMAC SHA256
や RSA
)。ヘッダーはJSONで表現され、次にBase64Urlを使用して文字列にエンコードされます。例:
{
"alg": "HS256",
"typ": "JWT"
}
エンコード後、次のような文字列を得ることができます:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. ペイロード
ペイロードには通常、エンティティ(通常はユーザー)に関する情報やその他のデータなど、一連のクレーム (claim) が含まれます。ペイロードには複数の事前定義のクレーム(登録済みクレームとも呼ばれる)やカスタムクレーム(プライベートクレーム)を含めることができます。
事前定義のクレームには以下が含まれます:
-
iss
(発行者): Issuer -
exp
(有効期限): Expiration time -
sub
(主題): Subject -
aud
(対象): Audience -
iat
(発行時刻): Issued time -
nbf
(未使用日時): Effective time -
jti
(JWT ID): JWTの一意の識別子
例えば、以下のようなペイロードがあります(JSON形式):
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
この情報もBase64Urlでエンコードされ、次のような文字列が得られます:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0
3. 署名
署名部は、上記でエンコードされた2つの文字列を署名するために使用され、メッセージが伝送中に改ざんされていないことを検証します。まず、(HMAC SHA256アルゴリズムを使用する場合は)鍵を指定し、次にヘッダーで指定されたアルゴリズムを使用してヘッダーとペイロードを署名します。
例えば、以下のヘッダーとペイロードがある場合:
HeaderEncoded.HeaderPayload
これらを鍵を使用して署名する擬似コードは以下のようになります:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey)
生成された署名された文字列は以下のようなものです:
dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
完全なJWT
これら3つの部分をドット (.) で区切って結合することで完全なJWTが形成されます:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
JWT標準は非常に柔軟であり、ペイロードに必要な情報を格納することができます(ただし、JWTのサイズを削減するためおよびセキュリティ上の理由から、機密情報を格納すべきではありません)。また、署名を通じて情報の整合性を保証します。
Go JWTライブラリのインストール
Goコミュニティでは、JWTを処理するためのgithub.com/golang-jwt/jwt
パッケージが提供されています。このライブラリをインストールするのは非常に簡単で、プロジェクトディレクトリで次のコマンドを実行するだけです:
go get -u github.com/golang-jwt/jwt/v5
インストールが完了すると、以下のようにimport
でそれを含めることができます:
import "github.com/golang-jwt/jwt/v5"
簡単なトークンの作成
Go言語とHS256アルゴリズムを使用して簡単なJWTトークンを作成するには、以下の手順に従う必要があります:
まず、HS256アルゴリズムを使用して新しいTokenオブジェクトを生成します:
token := jwt.New(jwt.SigningMethodHS256)
次に、SignedString
メソッドを使用して、このトークンの文字列表現を生成します。署名に使用するキーを渡します。
var mySigningKey = []byte("your-256-bit-secret")
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("エラーが発生しました: %v", err)
}
fmt.Println(strToken)
これにより、クレームを持たない簡単なトークンが生成されます。
パラメーター付きトークンの作成
JWTの主な機能の1つは情報を運ぶことです。この情報はクレームを介してトークンにエンコードされます。例えば、カスタムクレームを作成します:
// カスタムクレームの構造
type MyClaims struct {
jwt.RegisteredClaims
Username string `json:"username"`
Admin bool `json:"admin"`
}
// カスタムクレームを持つトークンを作成
claims := MyClaims{
RegisteredClaims: jwt.RegisteredClaims{},
Username: "my_username",
Admin: true,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 署名に使用する秘密鍵
var mySigningKey = []byte("your-256-bit-secret")
// 文字列形式でトークンを生成
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("エラーが発生しました: %v", err)
}
fmt.Println(strToken)
上記のコードでは、登録されたクレームとカスタム情報を含むMyClaims
構造体を定義しました。その後、引き続きSignedString
を使用してトークン文字列を生成しています。
トークンの解析と検証
JWTの解析と検証は非常に重要です。以下のように行うことができます:
// 同じMyClaims構造を使用します
tokenString := "your-JWT-string"
// tokenStringを解析するためにjwtパッケージが使用する関数を定義する必要があります
keyFunc := func(t *jwt.Token) (interface{}, error) {
// 予期しない署名メソッドが使用されていないことを検証
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("予期しない署名メソッド: %v", t.Header["alg"])
}
// jwtトークンのための秘密鍵を返します。署名時に使用したキーと整合する[]byte形式である必要があります
return mySigningKey, nil
}
// トークンの解析
claims := &MyClaims{}
parsedToken, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
log.Fatalf("解析エラー: %v", err)
}
if !parsedToken.Valid {
log.Fatalf("無効なトークン")
}
// この段階で、parsedTokenは検証されており、クレームを読むことができます
fmt.Printf("ユーザー: %s, 管理者: %v\n", claims.Username, claims.Admin)
上記のコードでは、token文字列、MyClaims
インスタンス、およびキー関数keyFunc
をjwt.ParseWithClaims
関数に提供しました。この関数は署名を検証し、トークンを解析し、トークンが有効であればclaims
変数を埋めます。