JSON Web Tokens (JWT)は、当事者間でクレームを安全に 送信する方法を提供します。これらは特に、サービス間で認証と認可情報を渡す必要がある マイクロサービスアーキテクチャで有用です。JWTは、ユーザー情報、権限、その他のクレームを 含むことができる自己完結型のトークンです。
JWT認証フローの詳細な説明については、 JWT認証フローガイドを参照してください。
JWTは3つの部分で構成されます(ライブ例についてはJWT.ioデバッガーを参照):
JWTの例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWTクレームの詳細については、 JWTクレームのドキュメントを参照してください。
スコープは、クライアントがAPIで実行できるアクションを決定する権限です。 スコープは、きめ細かなアクセス制御を実装する方法と考えてください。例えば:
read
スコープを持つread
とwrite
の両方のスコープを持つbackup
スコープを持つ実世界の例え:
room:access
- 自分の部屋のみにアクセスpool:access
- プールへのアクセスgym:access
- ジムへのアクセスall:access
- すべての施設への完全なアクセススコープは通常、resource:action
のようなパターンに従います。一般的な例:
api:read # APIへの読み取り専用アクセス
api:write # APIへの書き込みアクセス
users:create # ユーザーを作成する能力
admin:* # 完全な管理者アクセス
スコープは階層的にすることができます。例えば:
api:read
を必要とする場合、admin:*
を持つトークンも有効かもしれないスコープ階層の例:
admin:* # 完全な管理者アクセス(すべての管理者スコープを含む)
├── admin:read # 管理リソースの読み取り
├── admin:write # 管理リソースの変更
└── admin:delete # 管理リソースの削除
まず、APIに存在するスコープを定義します:
var JWTAuth = JWTSecurity("jwt", func() {
Description("スコープ付きJWT認証")
// 利用可能なすべてのスコープを定義
Scope("api:read", "APIリソースへの読み取りアクセス")
Scope("api:write", "APIリソースへの書き込みアクセス")
Scope("api:admin", "完全な管理者アクセス")
Scope("users:read", "ユーザープロファイルの読み取り")
Scope("users:write", "ユーザープロファイルの変更")
})
次に、各エンドポイントに必要なスコープを指定します:
var _ = Service("users", func() {
// ユーザー一覧 - 読み取りアクセスが必要
Method("list", func() {
Security(JWTAuth, func() {
// 読み取りアクセスのみ必要
Scope("users:read")
})
})
// ユーザー更新 - 書き込みアクセスが必要
Method("update", func() {
Security(JWTAuth, func() {
// 読み取りと書き込みの両方のアクセスが必要
Scope("users:read", "users:write")
})
})
// ユーザー削除 - 管理者アクセスが必要
Method("delete", func() {
Security(JWTAuth, func() {
Scope("api:admin")
})
})
})
トークンを生成する際に、付与されたスコープを含めます:
func GenerateUserToken(user *User) (string, error) {
// ユーザーロールに基づいてスコープを決定
var scopes []string
switch user.Role {
case "admin":
scopes = []string{"api:admin", "users:read", "users:write"}
case "editor":
scopes = []string{"users:read", "users:write"}
default:
scopes = []string{"users:read"}
}
claims := Claims{
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
IssuedAt: time.Now().Unix(),
Subject: user.ID,
},
Scopes: scopes, // トークンにスコープを含める
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(jwtSecret))
}
リクエストを処理する際に、トークンが必要なスコープを持っているか検証します:
func validateScopes(tokenScopes []string, requiredScopes []string) error {
// 効率的な検索のためにトークンのスコープのマップを作成
scopeMap := make(map[string]bool)
for _, scope := range tokenScopes {
scopeMap[scope] = true
}
// 特別なケース:管理者スコープはすべてのアクセスを許可
if scopeMap["api:admin"] {
return nil
}
// 各必要なスコープをチェック
for _, required := range requiredScopes {
if !scopeMap[required] {
return fmt.Errorf("必要なスコープがありません: %s", required)
}
}
return nil
}
命名規則
resource:action
)粒度
ドキュメント
セキュリティ
管理
まず、設計パッケージでJWTセキュリティスキームを定義します。
package design
import (
. "goa.design/goa/v3/dsl"
)
// JWTAuthはセキュリティスキームを定義
var JWTAuth = JWTSecurity("jwt", func() {
Description("JWT認証")
// 認可のためのスコープを定義
Scope("api:read", "APIへの読み取りアクセス")
Scope("api:write", "APIへの書き込みアクセス")
})
JWT認証は、特定のスコープ要件を持つ異なるレベルで適用できます。