1. JWT
-
JWTとは
- json Webトークンの公式定義はオープンスタンダード(rfc7519)であり、JSONオブジェクトとして当事者間で情報を安全に送信するためのコンパクトで自己完結型の方法を定義しています。この情報は検証および信頼できるためです。それはデジタル署名されています。jwtは、シークレット(HMACアルゴリズムを使用)を使用するか、RSAまたはECDSA公開キー、秘密キーを使用して署名できます。
- 一般的な説明では、JWTはJSON Webトークンと呼ばれ、JSON形式のWebアプリケーションでトークンとして使用されます。これは、当事者間で情報をJSONオブジェクトとして安全に送信するために使用されます。データの暗号化と署名は、送信プロセス中に完了することもできます。扱う。
-
JWTでできること
- 承認
- これは、JWTを使用する最も一般的なソリューションです。ユーザーが正常にログインすると、システムはJWTを生成し、それをユーザーに渡します。後続の各リクエストにはJWTが含まれ、ユーザーはトークンで許可されているルート、サービス、およびリソースにアクセスできます。シングルサインオンは、オーバーヘッドが小さく、さまざまなドメインで簡単に使用できるため、今日広く使用されているJWTの機能です。
- 情報交換
- JSON Webトークンは、当事者間で情報を安全に転送するための優れた方法です。JWTは署名できるため、送信者がそれを言った人であると確信できます。さらに、署名はヘッダーとペイロードを使用して計算されるため、コンテンツが改ざんされていないかどうかを確認することもできます。
- 承認
-
JWTを使用する理由
-
従来のセッション認証に基づく
- httpプロトコル自体はステートレスプロトコルであることがわかっています。つまり、ユーザーがユーザー認証のためにアプリケーションにユーザー名とパスワードを提供した場合、次にユーザーが要求したときに、ユーザーはユーザー認証を再度実行する必要があります。 、httpプロトコルによると、どのユーザーがリクエストを行ったかがわからないため、アプリケーションがどのユーザーがリクエストを行ったかを説明するために、サーバーに保存できるのはユーザーログイン情報のみです。このログイン情報は応答として、ブラウザに渡され、Cookieとして保存するように指示されます。これ
により、次のリクエストが行われたときにアプリケーションに送信できるため、アプリケーションはリクエストの送信元を識別できます。これは、従来のセッションベースの認証です。
- httpプロトコル自体はステートレスプロトコルであることがわかっています。つまり、ユーザーがユーザー認証のためにアプリケーションにユーザー名とパスワードを提供した場合、次にユーザーが要求したときに、ユーザーはユーザー認証を再度実行する必要があります。 、httpプロトコルによると、どのユーザーがリクエストを行ったかがわからないため、アプリケーションがどのユーザーがリクエストを行ったかを説明するために、サーバーに保存できるのはユーザーログイン情報のみです。このログイン情報は応答として、ブラウザに渡され、Cookieとして保存するように指示されます。これ
-
認定プロセス
- クッキーセッション
-
公開された問題
- 各ユーザーがアプリケーションによって認証された後、アプリケーションはユーザーの次の要求を容易にするためにサービスに記録を作成する必要があります。一般的に、セッションはメモリに保存され、認証されたユーザーの数が増えると、サーバーはコストが大幅に増加します
- ユーザーが認証された後、サーバーは認証レコードを作成します。認証レコードがメモリに保存されている場合、これは、ユーザーの次の要求もこのサーバーで要求する必要があることを意味します。これにより、許可されたソースを取得して配布できます。アプリケーションの形式では、対応する負荷分散機能が制限されます。これは、アプリケーションのスケーラビリティが制限されることも意味します。
- ユーザーIDはCookieに基づいているため、Cookieが傍受されると、ユーザーはクロスサイトリクエストの偽造攻撃に対して脆弱になります。
- フロントエンドとバックエンドの分離システムでは、さらに厄介です。フロントエンドとバックエンドの分離により、アプリケーションが分離された後の展開が複雑になります。通常、ユーザーは1つのリクエストに対して複数回転送する必要があります。セッションIDを使用してセッションIDをサーバーに毎回運ぶ場合、サーバーはユーザーにクエリを実行します。情報、同時に多数のユーザーがいる場合、この情報はサーバーのメモリに保存されるため、サーバーの負担が増大します。CSRF(クロスサイトリクエスト偽造攻撃、セッションはユーザー識別のためのCookieに基づいています。Cookieが傍受されると、ユーザーは非常にクロスサイトリクエスト偽造攻撃に対して脆弱であり、
sessionidは特徴的な値であり、表現された情報は十分に豊富ではなく、拡張も容易ではありません。また、バックエンドアプリケーションがマルチノード展開である場合は、セッション共有メカニズムを実装する必要があります。これは、クラスタリングには不便です。応用
-
JWT認証に基づく
- 認定プロセス
-
認定プロセス
- まず、フォームを介してユーザー名とパスワードをバックエンドインターフェイスに送信します。このプロセスは通常、HTTP POSTリクエストです。このメソッドは、機密情報が盗聴されるのを防ぐために、SSL暗号化送信(httpsプロトコル)を介して構築されます。
- バックエンドはユーザー名とパスワードを正常にチェックした後、ユーザーのIDとその他の情報をJWTペイロード(ロード)として使用し、bas64でエンコードしてヘッダーと接続し、JWT(トークン)を形成します。形成されたJWTは同じです。 111.zzz、xxx文字列
- トークンヘッド。ペイロード。singurater
- バックエンドは、フロントエンドへのログインが成功した結果としてJWTを返します。フロントエンドは返された結果をlocalStorageまたはsessionStorageに保存でき、フロントエンドはログアウト時に保存されたJWTを削除できます。
- フロントエンドは、リクエストのたびにHttpヘッダーのAuthorizationビットにJWTを配置します(XSSおよびXSRFの問題を解決します)HEADER
- バックエンドは、存在するかどうかをチェックし、存在する場合は、JWTの有効性を検証します。たとえば、署名が正しいかどうかをチェックします。トークンの有効期限が切れているかどうかをチェックします。トークンの受信者が自分自身であるかどうかをチェックします(オプション)
- 検証に合格すると、バックエンドはJWTに含まれているユーザー情報を使用して他の論理操作を実行し、対応する結果を返します。
-
JWTの利点
- 簡潔:データ量が少なく、伝送速度が非常に速いため、URL、POSTパラメーター、またはHTTPヘッダーを介して送信できます。
- 自己完結型:ロードにはユーザーが必要とするすべての情報が含まれているため、データベースへの複数のクエリを回避できます
- トークンはJSON暗号化の形式でクライアントに保存されるため、JWTはクロスランゲージであり、原則として、任意のWeb形式がサポートされます。
- サーバーにセッション情報を保存する必要はありません。これは、分散型マイクロサービスに特に適しています。
-
-
JWT構造
-
トークン構成
- ヘッダ
- ペイロード
- 署名(Sionature)
したがって、JWTは通常次のようになります:xxx.yyy.zzz
-
ヘッダー
ヘッダーは通常、トークンのタイプ(つまり、JWTと、Base64エンコーディングをJWT構造の最初の部分にするHMACSHA256やRSAなどの使用される署名アルゴリズム)の2つの部分で構成されます。
注:Base64も一種のエンコーディングです。つまり、元の外観に戻すことができます。暗号化プロセスではありません。{ "alg":"HS256", "typ":"JWT" }
-
ペイロード
トークンの2番目の部分はペイロードであり、エンティティ(通常はユーザー)およびその他のデータに関するクレームであるクレームが含まれています。同様に、Base64エンコーディングを使用して
JWT構造の2番目の部分を形成します{ "id":666, "name":"bitqian666", "age":19 }
-
署名の
最初の2つの部分は、Base64を使用してエンコードされます。つまり、フロントエンドは内部の情報のロックを解除できます。署名は、エンコードされたヘッダーとペイロード、および提供されたキーを使用してから、ヘッダーで指定された署名アルゴリズムを使用する必要があります(HS256)署名、署名の役割は、JWTがMuによって変更されていないことを確認することです -
署名の目的
署名プロセスの最後のステップは、実際には、ヘッダーとペイロードのコンテンツに署名して、コンテンツが改ざんされないようにすることです。誰かがヘッダーとペイロードの内容をデコード後に変更してからエンコードし、最後に前の署名の組み合わせを追加して新しいJWTを形成すると、サーバーは新しいヘッダーとペイロードによって形成された署名がJWTに添付されていると判断します。署名は異なります。
新しいヘッダーとペイロードに署名する場合、サーバーの暗号化に使用されるキーがわからない場合は、結果の署名も異なります。 -
情報セキュリティの問題
誰もがここで間違いなく質問します。Base64は一種のエンコーディングであり、リバーシブルであるため、私の情報が公開されますか?
はい。したがって、JWTでは、機密データを負荷に追加しないでください。たとえば、送信しているのはユーザーのユーザーIDです。この値は実際には機密性の高いコンテンツではなく、一般的には安全に知られています。ただし、パスワードなどのコンテンツをJWTに配置することはできません。ユーザーのパスワードをJWTに入力すると、悪意のあるサードパーティがBase64デコードを介してパスワードをすばやく知ることができます。したがって、JWTは、機密性の低い情報をWebアプリケーションに配信するのに適しています。JWTは、ユーザー認証および承認システムの設計、さらにはWebアプリケーションのシングルサインオンの実装にもよく使用されます。
-
-
JWTの問題と傾向
- JWTはデフォルトでは暗号化されていませんが、暗号化できます。元のトークンを生成した後、変更されたトークンを使用して再度暗号化できます。
- JWTが暗号化されていない場合、一部のプライベートデータはJWTを介して送信できません。
- JWTは、認証だけでなく、情報交換にも使用できます。JWTをうまく利用すると、データベースへのサーバー要求の数を減らすことができます。
- JWTの最大の欠点は、サーバーがセッション状態を保存しないため、使用中にトークンをキャンセルしたり、トークンのアクセス許可を変更したりできないことです。つまり、JWTが発行されると、有効期間中は有効なままになります。
- JWT自体に認証情報が含まれているため、情報が漏洩すると、誰でもトークンのすべての権限を取得できます。使い込みを減らすために、JWTの有効期間を長く設定しすぎないようにしてください。一部の重要な操作では、ユーザーは使用するたびに認証を受ける必要があります。
- JWTを無意味に更新すると、バックエンドはJWT分析を受信して有効期限を取得します。有効期限が近づくと、新しいトークンが生成されます(ビジネスデータは以前と同じです)。
- 使い込みや盗難を減らすために、JWTはコードの送信にHTTPプロトコルを使用することを推奨していませんが、送信に暗号化されたHTTPSプロトコルを使用することを推奨しています。
2. jwt暗号化および復号化ツール、使用
pom
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
ツール
package top.bitqian.config;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author echo lovely
* @date 2020/12/4 21:46
*/
public class JwtUtil {
//密钥(不能泄露 、可多次加盐加密再存在合适的地方)
public static final String SECRET = "defAu&TJHVhc$%WW^JJG";
//过期时间:秒
public static final int EXPIRE = 300;
/**
* JWT 添加至HTTP HEAD中的前缀
*/
private static final String JWT_SEPARATOR = "Bearer ";
/**
* 生成Token
* @param userName userName
* @param userPwd userPwd
* @return token token
* @throws Exception create token ex
*/
public static String createToken(String userName, String userPwd) throws Exception {
try {
//日历对象
//当前时间
Calendar nowTime = Calendar.getInstance();
//加30秒
nowTime.add(Calendar.SECOND, EXPIRE);
Date expireDate = nowTime.getTime();
//标头 可以不写,默认值就是下方所定义的map
Map<String, Object> map = new HashMap<>();
// 算法
map.put("alg", "HS256");
// 类型 jwt加密
map.put("typ", "JWT");
return JWT.create()
.withHeader(map)//头 有默认值 可以不写
//负载 业务数据
.withClaim("userPwd", userPwd)
.withClaim("userName", userName)
.withClaim("myId", "aa")
.withSubject("subject")//可以不写
.withIssuedAt(new Date())//签名时间
.withExpiresAt(expireDate)//过期时间
.sign(Algorithm.HMAC256(SECRET));//签名
} catch (Exception e) {
throw new Exception(e);
}
}
/**
* 验证Token
* @param token token
* @return token map
* @throws RuntimeException 凭证已过期,请重新登录
*/
public static Map<String, Claim> verifyToken(String token) throws RuntimeException {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt;
try {
jwt = verifier.verify(token);
}catch (Exception e){
throw new RuntimeException("凭证已过期,请重新登录");
}
return jwt.getClaims();
}
/**
* 解析Token
* @param token token
* @return claim
*/
public static Map<String, Claim> parseToken(String token){
DecodedJWT decodedJWT = JWT.decode(token);
return decodedJWT.getClaims();
}
}
テスト
public static void main(String[] args){
try {
// 创建token,传入参数
String token = JwtUtil.createToken("your token param", "your token param");
// 获取token
System.out.println("token=" + token);
//Thread.sleep(5000);
Map<String, Claim> map = JwtUtil.verifyToken(token);
//Map<String, Claim> map = JwtUtil.parseToken(token);
//遍历
for (Map.Entry<String, Claim> entry : map.entrySet()){
if (entry.getValue().asString() != null){
System.out.println(entry.getKey() + "===" + entry.getValue().asString());
}else {
System.out.println(entry.getKey() + "===" + entry.getValue().asDate());
}
}
}catch (Exception e){
e.printStackTrace();
}
}