JWT [何です]
JSONウェブトークン(JWT)は、最も人気のあるクロスドメイン認証ソリューションです。
JWTの公式ウェブサイトのアドレス:https://jwt.io/
口語的に言って、JWTは、ユーザーのIDトークンを表現することができ、トークンは、ユーザーは、APIへのアクセス権を持っているかどうかを確認するために、アイデンティティJWTのAPIユーザインタフェースのチェックに使用することができます。
JWTは、認証およびユーザー定義のパラメータのために必要なパラメータが含まれています(使用して秘密を使ってJWTをHMACのアルゴリズムを)、または使用してRSA またはECDSAを公開鍵/秘密鍵のペアがされた署名します。
[とき、あなたはJSONウェブトークンを使用する必要がありますか?]
-
認証:これはJWTプログラムの最も一般的な使用です。ユーザーがログオンすると、後続の各要求は、ユーザーがトークンがルーティング、サービスやリソースを可能にアクセスすることができ、JWTが含まれます。シングルサインオンは、その低いオーバーヘッドで今広く使われているJWTの関数であり、簡単に別のドメインを使用することができます。
-
情報交換:JSONウェブトークンは、当事者間の情報の伝達を確保するための良い方法です。JWTは、署名することができますので - 例えば、公開鍵/秘密鍵のペアを使用して - あなたは、送信者が、彼らが言う誰であるかを決定することができます。また、ヘッダおよびペイロード計算署名の使用は、あなたはまた、コンテンツが改ざんされていないことを確認することができます。
[JWT利点は何ですか?]
私たちは、伝統的な身元確認の方法を見て
- ユーザーは、サーバーにユーザー名とパスワードを送信します。
- サーバーの後ように、このようなユーザの役割、ログイン時間などの関連データを、保持しており、現在のセッション(セッション)で、検証されます。
- ユーザーにサーバーのリターンをSESSION_ID、ユーザーがクッキーを書き込みます。
- クッキー、バックサーバーへのsession_idすることにより、ユーザその後、すべての要求の意志。
- サーバーはセッション_受け取り、予め記憶されたデータを見つけ、したがって、ユーザーのIDを表します。
このモデルの問題は、スケーラビリティ(スケーリング)が良くないということです。スタンドアローン、それはサーバのクラスタ、またはクロスドメインのサービス指向アーキテクチャーの場合はもちろん、何の問題は、それがセッションを共有するデータを必要とせず、各サーバは、セッションを読むことができます。セッションストレージノードがハングアップした場合、全体のサービスはリスクが高く、非常に悪い経験、麻痺されます。
これとは対照的に、JWTの実装では、サーバはそれを保存しない、クライアントに格納されているユーザ情報です。でログインしているユーザーを確認するために、各リクエストトークンを考え持参、このサービスはステートレスになり、サーバークラスタも非常に良い拡張したものです。
[構成]トークンJWT
コンパクト形態では、(DOTによってJSONウェブトークン.
)組成物で区切られた3つの部品です。
- ヘッダーヘッド
- ペイロードペイロード
- 署名署名
このように、JWTは、一般的に次のように:
xxxxx.yyyyy.zzzzz
1.Headerヘッド
ヘッダは、典型的には 2つの部分から構成:トークンタイプ、すなわちJWT、および署名アルゴリズムは、例えば、HMACのSHA256またはRSAのために、使用されています。
例えば:
{ "ALG": "HS256" 、 "標準": "JWT" }
その後、JSONはれる符号化としてBase64Url JWTの第1の部分を形成します。
ペイロード2.Payload
ペイロードが必要とされる実際のデータ転送を格納するために使用されるJSONオブジェクトの一部です。JWTは、選択のための7つの公式の場を提供します。
-
ISS(発行者):発行者
-
EXP(有効期限):有効期限
-
サブ(被写体):テーマ
-
AUD(聴衆):対象読者
-
NBF(前ではなく):効果的な時間
-
IAT(で発行された):時間の問題
-
JTI(JWT ID):いいえ
公式の場に加えて、あなたも、このセクションでプライベートフィールドを定義することができ、以下は一例です。例えば:
{ "サブ": "1234567890" 、 "名前": "ジョン・ドウ" 、 "管理者":真 }
注、JWTのデフォルトは暗号化されていない、誰でも読むことができますので、このセクションで機密情報を入れないでください。JSONオブジェクトを文字列に変換Base64URLアルゴリズムを使用する必要があります。
3.Signature署名
署名は、データの改ざんを防止するため、最初の2つの部分の署名の一部です。
まず、あなたは鍵(秘密)を指定する必要があります。キーはサーバがユーザに開示することはできません知っていることです。次に、以下の式に従って署名を生成ヘッダ内で指定された署名アルゴリズム(デフォルトHMAC SHA256)を、使用。
HMACSHA256( base64UrlEncode(ヘッダ) + "" + base64UrlEncode(ペイロード)、 秘密)
メッセージを認証するために使用される署名は、秘密鍵署名トークンを使用する場合には、このプロセスの間に変更され、されていない、それは、送信者JWTは、それが人々を主張するものであることを確認することができます。
一緒にそれらのすべて3
出力はBase64でURL文字列は、標準的なXMLベースの(例えばSAMLなど)と比較して容易にHTML及びHTTP環境を通過し、よりコンパクトにすることができる3つの点によって分離されます。
以下はJWT、以前に符号化されたヘッダとペイロード、及び秘密署名の使用を示します。
あなたが実際にJWTこれらの概念を使用する場合は、使用することができますデバッガをjwt.io JWTをデコード検証および生成します。
[JSONウェブトークンがどのように動作しますか?]
認証では、ユーザーが正常に自分の資格情報を使用してログインすると、JSONウェブトークンを返します。トークンは証拠であるとして、セキュリティ上の問題を防ぐために非常に慎重でなければなりません。通常の状況で、あなたは予約に必要な時間トークンを超えないようにしてください。
ユーザが保護されたリソースまたはルーティングにアクセスしたいときはいつでも、ユーザエージェントがなければならない使用ベアラモードJWT、通常送信許可ヘッダ。:次のようにコンテンツのタイトル
認証:ベアラ<トークン>
いくつかのケースでは、これは何の状態のライセンスメカニズムになることはできません。ルーティングサーバーによる保護が確認Authorization
ヘッダ有効JWTのを 、存在する場合、ユーザは、保護されたリソースにアクセスすることを可能にします。JWTは、必要なデータが含まれている場合、それは必ずしもそうではないかもしれないが、あなたは、特定の操作の必要性を減らすためにデータベースを照会することができます。
標準の場合はAuthorization
ヘッダはトークン送信され、それはクッキーを使用しないため、クロスオリジンリソース共有(CORS)は、問題になることはありません。
次の図は、とやリソースにアクセスするためのJWTのAPIを取得する方法を示しています。
- アプリケーションは、認証サーバに認証を要求します
- 検証に成功したことを確認し、ユーザーID、リターン・トークン
- 保護されたリソースにアクセスするためのアクセストークンを使用するアプリケーション
【ASP.NetコアJWTを統合しました]
以前、我々はJWTの原理を導入、のは、asp.netコアの実用的なプロジェクトでJWTを統合してみましょう。
まず、私たちは、新しい空のWebプロジェクトデモasp.netコアを作成します
添加数据访问模拟api,ValuesController
其中api/value1是可以直接访问的,api/value2添加了权限校验特性标签 [Authorize]
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Demo.Jwt.Controllers { [ApiController] public class ValuesController : ControllerBase { [HttpGet] [Route("api/value1")] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value1" }; } [HttpGet] [Route("api/value2")] [Authorize] public ActionResult<IEnumerable<string>> Get2() { return new string[] { "value2", "value2" }; } } }
添加模拟登陆,生成Token的api,AuthController
这里模拟一下登陆校验,只验证了用户密码不为空即通过校验,真实环境完善校验用户和密码的逻辑。
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; namespace Demo.Jwt.Controllers { [Route("api/[controller]")] [ApiController] public class AuthController : ControllerBase { [AllowAnonymous] [HttpGet] public IActionResult Get(string userName, string pwd) { if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd)) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Name, userName) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: Const.Domain, audience: Const.Domain, claims: claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) }); } else { return BadRequest(new { message = "username or password is incorrect." }); } } } }
Startup添加JWT验证的相关配置
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Text; namespace Demo.Jwt { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //添加jwt验证: services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否验证Issuer ValidateAudience = true,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ClockSkew = TimeSpan.FromSeconds(30), ValidateIssuerSigningKey = true,//是否验证SecurityKey ValidAudience = Const.Domain,//Audience ValidIssuer = Const.Domain,//Issuer,这两项和前面签发jwt的设置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到SecurityKey }; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { ///添加jwt验证 app.UseAuthentication(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
最后把代码里面用到的一些相关常量也粘贴过来,Const.cs
namespace Demo.Jwt { public class Const { /// <summary> /// 这里为了演示,写死一个密钥。实际生产环境可以从配置文件读取,这个是用网上工具随便生成的一个密钥 /// </summary> public const string SecurityKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB"; public const string Domain = "http://localhost:5000"; } }
到这里,已经是我们项目的所有代码了。
如果需要完整的项目代码,Github地址:https://github.com/sevenTiny/Demo.Jwt
【JWT测试】
我们找一个趁手的工具,比如fiddler,然后把我们的web站点运行起来
首先调用无权限的接口:http://localhost:5000/api/value1
正确地返回了数据,那么接下来我们测试JWT的流程
1. 无权限
首先我们什么都不加调用接口:http://localhost:5000/api/value2
返回了状态码401,也就是未经授权:访问由于凭据无效被拒绝。 说明JWT校验生效了,我们的接口收到了保护。
2.获取Token
调用模拟登陆授权接口:http://localhost:5000/api/Auth?userName=zhangsan&pwd=123
这里的用户密码是随便写的,因为我们模拟登陆只是校验了下非空,因此写什么都能通过
成功得到了响应
然后我们得到了一个xxx.yyy.zzz 格式的 token 值。我们把token复制出来
3.在刚才401的接口请求HEADER中添加JWT的参数,把我们的token加上去
再次调用我们的模拟数据接口,但是这次我们加了一个HEADER:http://localhost:5000/api/value2
把内容粘出来
User-Agent: Fiddler Host: localhost:5000 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiIxNTYwMzQ1MDIxIiwiZXhwIjoxNTYwMzQ2ODIxLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiemhhbmdzYW4iLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjUwMDAifQ.x7Slk4ho1hZc8sR8_McVTB6VEYLz_v-5eaHvXtIDS-o
这里需要注意 Bearer 后面是有一个空格的,然后就是我们上一步获取到的token
嗯,没有401了,成功返回了数据
4.JWT的Token过期
我们且倒一杯开水,坐等30分钟(我们代码中设置的过期时间),然后再次调用数据接口:http://localhost:5000/api/value2
又变成了401,我们看下详细的返回数据
这里有标注,错误描述 token过期,说明我们设置的token过期时间生效了
【结束】
到这里,我们JWT的简介以及asp.net core 集成JWT已经完美完成,当然了这只是一个demo,在实际的应用中需要补充和完善的地方还有很多。
如果想要完整项目源码的,可以参考地址:https://github.com/sevenTiny/Demo.Jwt
如果有幸能帮助到你,高抬贵手点个star吧~