記事のディレクトリ
クロスドメイン認証の問題
インターネットサービスは、ユーザー認証なしで行うことはできません。一般的なプロセスは以下です:
- ユーザーは、サーバーにユーザー名とパスワードを送信します。
- サーバーの後ように、このようなユーザの役割、ログイン時間などの関連データを、保持しており、現在のセッション(セッション)で、検証されます。
- ユーザーにサーバーのリターンをSESSION_ID、ユーザーがクッキーを書き込みます。
- その後、ユーザがクッキーにより、すべての要求の意志、サーバーへのSESSION_IDバック。
- サーバーは、SESSION_IDを受け取り、予め記憶されたデータを見つけ、したがって、ユーザーのIDを表します。
このモデルの問題は、スケーラビリティ(スケーリング)が良くない、ということです。もちろんスタンドアロンそれはサーバのクラスタ、またはクロスドメインのサービス指向アーキテクチャーであれば、何の問題は、それがセッションを共有するデータを必要とせず、各サーバは、セッションを読むことができます。
たとえば、AとBのサイトサイトは、同社のサービスに関連付けられています。今では自動的にログに記録する別のウェブサイトを訪問し、その後、長いウェブサイトのログイン中のユーザーのように、必要とし、達成するためにどのように頼みますか?
- 一つの解決策は、データベースまたは他の永続化層に書き込まれたセッションデータの永続性、です。リクエストの様々なサービスの領収書は、すべての永続層にデータを要求しました。このアプローチの利点は明白な構造で、欠点は、エンジニアリングよりも大きくなっています。さらに、吊り持続性の場合は、単一障害点があります。
- 別のオプションは、サーバーがセッションデータは保存されませんし、すべてのデータがクライアントに格納され、サーバーへの各リクエストのバックだけです。JWTは、このプログラムの代表です。
JWTは何ですか?
(JWT)トークンJSONウェブは、オープンスタンダードに基づいたネットワークアプリケーション実行環境の間を通過するために文をあるJSON((7519 RFC)。トークンは、コンパクトで安全な、特にシングルに分散したサイトのためになるように設計されてサインオン(SSO)シナリオ.JWT文は一般的に提供するために使用されており、サービスプロバイダは、サーバからリソースを取得するために識別情報を認証されたユーザ識別情報との間で転送し、あなたもでなければならないいくつかの余分な他のビジネス・ロジックを追加することができますステートメント情報は、認証トークンを直接使用することができる、または暗号化されてもよいです。
JWT原理
認証サーバは、署名後にJSONオブジェクトを生成した後、データのいずれかを保存せずに、ユーザにサーバ・セッションをバックを送りました。このように拡張を実装するのは簡単、となっステートレスにサーバーをもたらします。
JWTの使用シナリオ
ここでは、2つのJWTのアプリケーションシナリオは以下のとおりです。
- 認証:JWTこれは最も一般的なシナリオです。ユーザーが正常にログインすると、各要求が許可されたサービスやリソースへのユーザーアクセスを許可する、JWTをもたらすでしょう。使いやすいです、その小さなオーバーヘッドカットのので、現在広くシングルサインオン(シングルサインオン)で使用されています。
- 情報交換:JWTは、異なる組織間で情報を交換するための素晴らしい方法です。JWTは、(例えば、公開鍵/秘密鍵のペア経由)に署名することができますので、あなたは必ず送信者があることを、彼らはアイデンティティの主張を言うことができます。また、署名はヘッダーとペイロードを使用して計算され、コンテンツが改ざんされていることを確認することができます。
JWTのデータ構造
扁平形はJWTによって行われる.
パーティションの3つの部分からなる組成物であって、それらは:
Header
Payload
Signature
そのため、見た目のJWT通常、次の形式:
xxxxx.yyyyy.zzzzz
ヘッダ
ヘッドは2つの部分から成ります:
- トークンタイプ、すなわち、JWT。
- 例えばHMAC SHA256またはRSAなどの署名アルゴリズム、。
Aヘッダーの例:
{
"alg": "HS256",
"typ": "JWT"
}
その後、上記の目的は、によってJSONあるBase64Url
JWTの第1の部分を符号化します。
ペイロード
ロードローカルストレージが有効な情報です。名前は、航空機上に担持されたそのような物品に特異的に指し、これらの効果的な情報は、3つの部分から構成され
- 標準的な登録届出書
- 公式声明
- Privateステートメント
標準的な登録届出書:
- ISS:JWTの発行者
- サブ:ユーザーのためのJWT
- AUD:受信側JWT
- EXP:JWT有効期限、有効期限は、時間の問題よりも大きくなければなりません。
- NBF:JWTの前に定義されてどのような時間は利用できません。
- IAT:時間の問題JWT
- JTI:JWT独自のアイデンティティは、主にリプレイ攻撃を避けるために、ワンタイムトークンとして使用されています。
Publicステートメント:
公共の宣言は任意の情報、必要な情報やその他のビジネスニーズを追加するには、ユーザに関する一般的な情報を追加できますが、機密性の高い情報を追加するために、クライアントの一部を解読することができますので、お勧めしません。
Privateステートメント:
base64では、情報の一部が平文として分類することができることを意味し、対称暗号化解除されているため、Privateステートメントは、共通の定義としての文の提供者と消費者であるが、一般的に、機密情報を保存することは推奨されません。
ペイロード例:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
続いて、ペイロードが通過するBase64Url
JWTの第2の部分をコードします。
署名
ヘッダーがエンコードされ、使用する署名の必要性、ペイロードは、エンコードされた、キーを作成し、ヘッダーは、アルゴリズムを指定しました。
あなたはHMAC SHA256アルゴリズムを使用する場合、署名はによって生成されます。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
あなたが実際に多くの概念を超える場合は、このツールはhttps://jwt.io/ JWTのゲームをプレイすることにより、提供することができます。下図のように:
JWTの使用
JWTクライアントがクッキーに保存することができ、サーバーが返すが、またのlocalStorageに保存することができ受けます。
その後、毎回クライアントとサーバの通信は、JWTを持参しなければなりません。あなたはクッキーの中に置くことができ、自動的に送信されますが、より良いアプローチは、HTTPリクエストの内側のAuthorizationヘッダフィールドを置くことですので、これは、クロスドメインにすることはできません。
Authorization: Bearer <token>
別のアプローチは、内部にデータボリュームにするとき、クロスドメイン、JWT POST要求することです。
JWTいくつかの機能
- JWTのデフォルトは暗号化されていませんが、また、暗号化することができます。オリジナルのトークンを生成した後、それは一度キーで再暗号化することができます。
- 暗号化なしのJWTの場合、秘密データは、JWTに書き込むことができません。
- JWTだけでなく、認証に使用することができ、それはまた、情報を交換するために使用することができます。JWTの効果的な使用は、サーバーの数は、データベースを削減することができる照会します。
- JWT最大の欠点は、サーバがセッション状態は保存されませんので、トークンは当然に廃止、またはトークンの権限を変更することができない、というのです。サーバは追加のロジックを展開する場合を除きすなわち、発行JWTたら、満期日まで有効のままになります。
- JWT自体は開示されたときに、誰もがトークンのすべての権限を取得することができ、認証情報が含まれています。盗難を減らすために、JWTの妥当性が比較的短く設定する必要があります。より重要な権利のいくつかのために、再び使用中のユーザを認証する必要があります。
- 盗難を減らすために、JWTは、HTTPSプロトコルを使用するように、HTTPプロトコルの伝送符号を使用してはなりません。
サンプルコード
囲碁言語バージョン:
package util
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
)
var ErrVerifyFailed = fmt.Errorf("verify failed")
//https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac
func CreateToken(claims jwt.MapClaims, privateKey []byte) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
block, _ := pem.Decode(privateKey)
if block == nil {
return "", errors.New("private key error")
}
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", err
}
return token.SignedString(priv)
}
//https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac
func VerifyToken(tokenString string, publicKey []byte) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
if token.Header["alg"] != "RS512" {
return nil, fmt.Errorf("unexpected siging alg: %v", token.Header["alg"])
}
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, ErrVerifyFailed
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, ErrVerifyFailed
}
pub := pubInterface.(*rsa.PublicKey)
return pub, nil
})
if err != nil {
return nil, ErrVerifyFailed
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, ErrVerifyFailed
}
参考資料:
- https://jwt.io/introduction/
- https://www.jianshu.com/p/576dbf44b2ae
- https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html