[.NETのコアプロジェクトの戦闘 - 統一認証基盤]章XII認可論文 - JWTの生成と検証プロセスの深い理解

[.NETのコア事業の戦闘 - 統一認証基盤]オープニングとディレクトリ

記事に基づいて説明しIds4たシナリオの使用から、パスワード認証モードを、原則として分析、カスタムシステム統合、完全なアカウントは、パスワード認証モードの内容を説明し、最後にここ3つの思考を与え、これが最初のためにと思うだろう問題の説明詳細なIds4access_tokenはを生成する方法の、そしてどのようにaccess_tokenはの妥当性を検証するために、そして最終的に私たちは、あなたがそれを開始するとき、JAVAがあるだろう、それにもかかわらず学ばなかった、(を達成するためにJAVAを使用することを望んでいた外部インターフェイスを実装する.NET WEBAPIを使用JAVAのケースを使用して、友人によると、私は)ケースを実装するために書きました。

.netcoreプロジェクト戦闘交換基(637 326 624)、興味のある友人は、グループで議論交換することができます。

A、JWTのプロフィール

  1. JWTは何ですか?
    JSONウェブトークン(JWT)はJSONオブジェクトが確実に当事者間で情報を転送するようにするための、コンパクト、自己完結型の方法を規定するオープン規格(RFC 7519)です。それはデジタル署名されたので、この情報は、検証し、信頼することができます。

  2. JWTを使用する場合は?

1)要求がトークン缶によって、トークンからの署名が含まれた後、長いユーザのログインシステムとして一度、より一般的な使用シナリオで認証は、また、シングルサインオンを実現するために使用されます。

2)、安全送信情報に鍵を用いて情報を交換するために、メッセージが改ざんされたかどうかを送信者が配置されているかを知ることができます。

  1. JWT構造は何ですか?

JSONウェブトークンは、それらの間のドット(。)によって接続された3つの部分で構成されています。これらの三つの部分は、以下のとおりです。

  • ヘッダ
  • ペイロード
  • 署名

ヘッダ
トークンタイプ(「JWT」)アルゴリズムと(例えば:HMAC SHA256またはRSAなど)名:ヘッダは、通常、2つの部分からなります。

例えば:

{
    "alg": "RS256",
    "typ": "JWT"
}

その後、Base64でエンコードされたJSONは、JWTのこの最初の部分を取得します

ペイロード

第二の部分は、宣言(必須)が含まJWTペイロードです。声明は、エンティティ(通常はユーザー)およびその他のデータに関する文です。登録し、パブリックとプライベート:免責事項3つのタイプがあります。

  • 登録の請求:文の定義済みのセットがあり、これらは必須ではありませんが、推奨しました。例えば:ISS(発行者)、EXP(有効期限)、サブ(被験者)、AUD(視聴者)等が挙げられます。
  • 公共の主張は:自由に定義することができます。
  • プライベートの請求:その当事者間で共有情報にステートメントを使用するか、または開示する同意をして登録されていません。

次に例を示します。

{
 "nbf": 1545919058,
 "exp": 1545922658,
 "iss": "http://localhost:7777", "aud": [ "http://localhost:7777/resources", "mpc_gateway" ], "client_id": "clienta", "sub": "1", "auth_time": 1545919058, "idp": "local", "nickname": "金焰的世界", "email": "[email protected]", "mobile": "13888888888", "scope": [ "mpc_gateway", "offline_access" ], "amr": [ "pwd" ] }

JWTを得るための第二の部分のためのペイロードのBase64エンコード

彼らは暗号化されている場合を除き、JWTのヘッダやペイロードに機密情報を置かないように注意してください。

署名

为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的                那个,然对它们签名即可。

例えば:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

署名は、秘密鍵署名を使用してトークンを、メッセージが配信中に変更されていないことを確認するために使用され、それが確認できるようにその主張送信者の送信者JWT。

二、IdentityServer4はのJWTを生成する方法ですか?

理解ではJWT、基本的な概念、私たちが知りたいことはJWT、我々は暗号化のための独自のキーを使用する方法の方法ですどのように生成するには、どのような暗号化です。

IdentityServer4暗号化方式?

Ids4現在は使用されRS256、非対称的に秘密署名を使用して、公開鍵に署名するクライアントをテストします。一部の人々は、我々が生成し、求めることができるIds4何も設定証明書がない場合、その理由もアップ実行できますか?ここでは、証明書の使用を説明し、しなければならないIds4証明書の暗号化プロセスを使用しています。

図1に示すように、ローディング証明

Ids4以下のための一時的な証明書のデフォルトのtoken生産を、コードを使用して  .AddDeveloperSigningCredential()自動的に生成するためにここにtempkey.rsaデフォルトの設定を使用して、プロジェクトのルートディレクトリにこのファイルを表示することができますので、もし、証明書ファイルを、コードなどは以下のとおりです。

public static IIdentityServerBuilder AddDeveloperSigningCredential(this IIdentityServerBuilder builder, bool persistKey = true, string filename = null) { if (filename == null) { filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.rsa"); } if (File.Exists(filename)) { var keyFile = File.ReadAllText(filename); var tempKey = JsonConvert.DeserializeObject<TemporaryRsaKey>(keyFile, new JsonSerializerSettings { ContractResolver = new RsaKeyContractResolver() }); return builder.AddSigningCredential(CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId)); } else { var key = CreateRsaSecurityKey(); RSAParameters parameters; if (key.Rsa != null) parameters = key.Rsa.ExportParameters(includePrivateParameters: true); else parameters = key.Parameters; var tempKey = new TemporaryRsaKey { Parameters = parameters, KeyId = key.KeyId }; if (persistKey) { File.WriteAllText(filename, JsonConvert.SerializeObject(tempKey, new JsonSerializerSettings { ContractResolver = new RsaKeyContractResolver() })); } return builder.AddSigningCredential(key); } }

同じ缶が使用する証明書を設定しない理由も理解することができます。

注:実稼働環境では、我々は自分の資格情報を設定するために使用したいです。

我々はすでに証明書を持っている場合は、証明書が生成される方法として、達成するために次のコードを使用することができ、オンライン情報の多くは、ここに提示されていません。

 .AddSigningCredential(new X509Certificate2(Path.Combine(basePath,"test.pfx"),"123456"));

次のように続いて、証明書情報を注入します。

builder.Services.AddSingleton<ISigningCredentialStore>(new DefaultSigningCredentialsStore(credential));
            builder.Services.AddSingleton<IValidationKeysStore>(new DefaultValidationKeysStore(new[] { credential.Key }));

あなたは、このような検査符号と暗号化などのプロジェクトにおける証明書関連業務、の背中を使用することができます。

2、暗号化証明書の使用

パートIは、チェックすることにより、モデル、プロセスの詳細な説明、すべてのライセンス情報を、パスワードを記述し、Claimビルドが完了した後、彼らが生成し始めtoken、次のようにコアコードです。

public virtual async Task<string> CreateTokenAsync(Token token) { var header = await CreateHeaderAsync(token); var payload = await CreatePayloadAsync(token); return await CreateJwtAsync(new JwtSecurityToken(header, payload)); } //使用配置的证书生成JWT头部 protected virtual async Task<JwtHeader> CreateHeaderAsync(Token token) { var credential = await Keys.GetSigningCredentialsAsync(); if (credential == null) { throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); } var header = new JwtHeader(credential); // emit x5t claim for backwards compatibility with v4 of MS JWT library if (credential.Key is X509SecurityKey x509key) { var cert = x509key.Certificate; if (Clock.UtcNow.UtcDateTime > cert.NotAfter) {//如果证书过期提示 Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", cert.Subject, cert.NotAfter.ToString(CultureInfo.InvariantCulture)); } header["x5t"] = Base64Url.Encode(cert.GetCertHash()); } return header; } //生成内容 public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, ILogger logger) { var payload = new JwtPayload( token.Issuer, null, null, clock.UtcNow.UtcDateTime, clock.UtcNow.UtcDateTime.AddSeconds(token.Lifetime)); foreach (var aud in token.Audiences) { payload.AddClaim(new Claim(JwtClaimTypes.Audience, aud)); } var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod); var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope); var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json); var normalClaims = token.Claims .Except(amrClaims) .Except(jsonClaims) .Except(scopeClaims); payload.AddClaims(normalClaims); // scope claims if (!scopeClaims.IsNullOrEmpty()) { var scopeValues = scopeClaims.Select(x => x.Value).ToArray(); payload.Add(JwtClaimTypes.Scope, scopeValues); } // amr claims if (!amrClaims.IsNullOrEmpty()) { var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray(); payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues); } // deal with json types // calling ToArray() to trigger JSON parsing once and so later // collection identity comparisons work for the anonymous type try { var jsonTokens = jsonClaims.Select(x => new { x.Type, JsonValue = JRaw.Parse(x.Value) }).ToArray(); var jsonObjects = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Object).ToArray(); var jsonObjectGroups = jsonObjects.GroupBy(x => x.Type).ToArray(); foreach (var group in jsonObjectGroups) { if (payload.ContainsKey(group.Key)) { throw new Exception(string.Format("Can't add two claims where one is a JSON object and the other is not a JSON object ({0})", group.Key)); } if (group.Skip(1).Any()) { // add as array payload.Add(group.Key, group.Select(x => x.JsonValue).ToArray()); } else { // add just one payload.Add(group.Key, group.First().JsonValue); } } var jsonArrays = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Array).ToArray(); var jsonArrayGroups = jsonArrays.GroupBy(x => x.Type).ToArray(); foreach (var group in jsonArrayGroups) { if (payload.ContainsKey(group.Key)) { throw new Exception(string.Format("Can't add two claims where one is a JSON array and the other is not a JSON array ({0})", group.Key)); } var newArr = new List<JToken>(); foreach (var arrays in group) { var arr = (JArray)arrays.JsonValue; newArr.AddRange(arr); } // add just one array for the group/key/claim type payload.Add(group.Key, newArr.ToArray()); } var unsupportedJsonTokens = jsonTokens.Except(jsonObjects).Except(jsonArrays); var unsupportedJsonClaimTypes = unsupportedJsonTokens.Select(x => x.Type).Distinct(); if (unsupportedJsonClaimTypes.Any()) { throw new Exception(string.Format("Unsupported JSON type for claim types: {0}", unsupportedJsonClaimTypes.Aggregate((x, y) => x + ", " + y))); } return payload; } catch (Exception ex) { logger.LogCritical(ex, "Error creating a JSON valued claim"); throw; } } //生成最终的Token protected virtual Task<string> CreateJwtAsync(JwtSecurityToken jwt) { var handler = new JwtSecurityTokenHandler(); return Task.FromResult(handler.WriteToken(jwt)); }

これらの原則を知って、私たちは正確に知ることができるようになりますがaccess_token、それらのものを入れている、と私たちはどのように生成された確認することができますToken

第三には、どのようにaccess_tokenはの妥当性を検証するには?

生産する方法を知っている、主な目的は、当社のサーバーインターフェースを向けることであるが、サーバがそれを保護するために、資源の配分限りコードの下に追加されている理由の安全を守るためにどのようにでしょうか?

services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
            {
               options.Authority ="http://localhost:7777";
               options.RequireHttpsMetadata = false; options.ApiName = "Api1"; options.SaveToken = true; }); //启用授权 app.UseAuthentication();

前にこれを理解するには、我々はシステムがマップの使用は、プロセスをよく理解することができ、検証プロセスを、行う理解する必要があります。

IMG
読んだ後、突然の光を見ていませんか?ここではあなたがよく理解することができ/.well-known/openid-configuration/jwks、元の証明書を公開鍵情報である介してアクセスされ、/.well-known/openid-configuration使用するすべてのクライアントへの暴露、安全なセックスは、非対称暗号化の原則、秘密鍵暗号は、公開鍵のみを検証することができることを確実にするために使用されますそう何のキー漏れの問題はありません。

わずか数コードが、彼らは我々がコーディング数を減らし、Ids4の良いパッケージを示す、そんなに物事を行っています。私たちのプロジェクトがない場合、これは、誰かが聞いてきますされ.netcore、その後、どのようにゲートウェイそれへのアクセス?

インターネットは、Pythonの例を持っていたWeb APIのPythonを保護するためにIdentity Serverの4(JWKSエンドポイントとRS256アルゴリズム)を使用します

私はもともと達成するためにJavaを使用することを計画し、私はJavaのそれを達成するためになり、友人を残して、書き方を忘れている触れていないが、原理は同じです。

今、私が持っていたwebapiサーバ側のインタフェースを開発するための例を、次にインタフェースのコンテンツを保護するためにIds4を使用しています。

新建一个webapi项目,项目名称Czar.AuthPlatform.WebApi,为了让输出的结果为json,我们需要在WebApiConfig增加config.Formatters.Remove(config.Formatters.XmlFormatter);代码,然后修改默认的控制器ValuesController,修改代码如下。

[Ids4Auth("http://localhost:6611", "mpc_gateway")]
public IEnumerable<string> Get() { var Context = RequestContext.Principal; return new string[] { "WebApi Values" }; }

为了保护api安全,我们需要增加一个身份验证过滤器,实现代码如下。

using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace Czar.AuthPlatform.WebApi { public class Ids4AuthAttribute : AuthorizationFilterAttribute { /// <summary> /// 认证服务器地址 /// </summary> private string issUrl = ""; /// <summary> /// 保护的API名称 /// </summary> private string apiName = ""; public Ids4AuthAttribute(string IssUrl,string ApiName) { issUrl = IssUrl; apiName = ApiName; } /// <summary> /// 重写验证方式 /// </summary> /// <param name="actionContext"></param> public override void OnAuthorization(HttpActionContext actionContext) { try { var access_token = actionContext.Request.Headers.Authorization?.Parameter; //获取请求的access_token if (String.IsNullOrEmpty(access_token)) {//401 actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); actionContext.Response.Content = new StringContent("{\"errcode\":401,\"errmsg\":\"未授权\"}"); } else {//开始验证请求的Token是否合法 //1、获取公钥 var httpclient = new HttpClient(); var jwtKey= httpclient.GetStringAsync(issUrl + "/.well-known/openid-configuration/jwks").Result; //可以在此处缓存jwtkey,不用每次都获取。 var Ids4keys = JsonConvert.DeserializeObject<Ids4Keys>(jwtKey); var jwk = Ids4keys.keys; var parameters = new TokenValidationParameters { //可以增加自定义的验证项目 ValidIssuer = issUrl, IssuerSigningKeys = jwk , ValidateLifetime = true, ValidAudience = apiName }; var handler = new JwtSecurityTokenHandler(); //2、使用公钥校验是否合法,如果验证失败会抛出异常 var id = handler.ValidateToken(access_token, parameters, out var _); //请求的内容保存 actionContext.RequestContext.Principal = id; } } catch(Exception ex) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); actionContext.Response.Content = new StringContent("{\"errcode\":401,\"errmsg\":\"未授权\"}"); } } } public class Ids4Keys { public JsonWebKey[] keys { get; set; } } }

代码非常简洁,就实现了基于Ids4的访问控制,现在我们开始使用PostMan来测试接口地址。

我们直接请求接口地址,返回401未授权。

然后我使用Ids4生成的access_token再次测试,可以得到我们预期结果。

为了验证是不是任何地方签发的token都可以通过验证,我使用其他项目生成的access_token来测试,发现提示的401未授权,可以达到我们预期结果。

现在就可以开心的使用我们熟悉的webapi开发我们的接口了,需要验证的地方增加类似[Ids4Auth("http://localhost:6611", "mpc_gateway")]代码即可。

使用其他语言实现的原理基本一致,就是公钥来验签,只要通过验证证明是允许访问的请求,由于公钥一直不变(除非认证服务器更新了证书),所以我们请求到后可以缓存到本地,这样验签时可以省去每次都获取公钥这步操作。

四、总结

本篇我们介绍了JWT的基本原理和Ids4JWT实现方式,然后使用.NET webapi实现了使用Ids4保护接口,其他语言实现方式一样,这样我们就可以把网关部署后,后端服务使用任何语言开发,然后接入到网关即可。

この知識によって、気持ちはしないIds4、それより深く理解できますか?JWT本当に便利な、しかし、我々はいくつかの特別なシーンをしたいTokenすぐに有効期間内で手動設定の失敗を経て、既存のに従って、Ids4認証、それを達成するためにどのようにして、それを行うための方法はありませんか?私は必須実装する方法を説明するために、次のいずれかになりますtoken失敗したことを、それを楽しみにしてください。

おすすめ

転載: www.cnblogs.com/lhxsoft/p/11945319.html