- اگر از JWE استفاده نمیکنید، بهتر است اطلاعات حساسی مانند شماره تلفن کاربر (و شاید در مواردی حتی آیدی کاربر) را در بدنه توکن قرار ندهیم چرا که قابل خوانده شدن است (که در این صورت استفاده از Guid برای آیدی کاربر می تواند کمی مفید باشد چرا که حداقل آیدی بقیه کاربران قابل پیش بینی نمیباشد).
- توکن JWT هیچ امنیتی در برابر خوانده شدن ندارد؛ ولی به لطف امضای (signature) آن، در برابر تغییر محتوا، ایمن است؛ چرا که در صورت تغییر محتوای آن، دیگر مقدار hash محتوا با امضای آن همخوانی نداشته و عملا از اعتبار ساقط میگردد.
var secretKey = Encoding.UTF8.GetBytes("LongerThan-16Char-SecretKey"); // must be 16 character or longer var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature); var encryptionkey = Encoding.UTF8.GetBytes("16CharEncryptKey"); //must be 16 character var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256); var claims = new List<Claim> { new Claim(ClaimTypes.Name, "UserName"), //user.UserName new Claim(ClaimTypes.NameIdentifier, "123"), //user.Id }; var descriptor = new SecurityTokenDescriptor { Issuer = _siteSetting.JwtSettings.Issuer, Audience = _siteSetting.JwtSettings.Audience, IssuedAt = DateTime.Now, NotBefore = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.NotBeforeMinutes), Expires = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.ExpirationMinutes), SigningCredentials = signingCredentials, EncryptingCredentials = encryptingCredentials, Subject = new ClaimsIdentity(claims) }; var tokenHandler = new JwtSecurityTokenHandler(); var securityToken = tokenHandler.CreateToken(descriptor); string encryptedJwt = tokenHandler.WriteToken(securityToken);
در ادامه لازم است در مرحله اعتبار سنجی و رمزگشایی توکن در سمت سرور، کلید و الگوریتم لازم را به آن معرفی کنیم تا middleware مربوطه بتواند توکن دریافتی را رمزگشایی و سپس اعتبار سنجی کند. بدین منظور در متد ConfigureServices کلاس Startup.cs خواهیم داشت:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { var secretkey = Encoding.UTF8.GetBytes("LongerThan-16Char-SecretKey"); var encryptionkey = Encoding.UTF8.GetBytes("16CharEncryptKey"); var validationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.Zero, // default: 5 min RequireSignedTokens = true, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(secretkey), RequireExpirationTime = true, ValidateLifetime = true, ValidateAudience = true, //default : false ValidAudience = "MyWebsite", ValidateIssuer = true, //default : false ValidIssuer = "MyWebsite", TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey) }; options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = validationParameters; });
کد بالا مانند کد فعال سازی احراز هویت توسط JWT معمولی در ASP.NET Core است؛ با این تفاوت که:
ابتدا آرایه بایتی همان کلید رمزنگاری (encryptionkey) که قبلا توکن را با آن رمزنگاری کرده بودیم، گرفته شده و سپس توسط مقداردهی خاصیت TokenDecryptionKey کلاس TokenValidationParameters، معرفی شده است.
ولی شاید این سؤال برایتان پیش آید که چرا الگوریتم رمزنگاری مشخص نشده است؟ پس سرور از کجا میفهمد که این توکن بر اساس چه الگوریتمی رمزنگاری شده است؟
دلیل آن این است که به هنگام تولید توکن، اسم الگوریتم مربوطه، داخل بخش header توکن نوشته میشود. اگر تصویر قبل را مشاهده کنید مقدار header توکن به شرح زیر است.
{ "alg": "A128KW", "enc": "A128CBC-HS256", "typ": "JWT" }
پس سرور بر اساس این قسمت از توکن (header)، که هیچگاه رمزنگاری نمیشود، میفهمد که توسط چه الگوریتمی باید توکن را رمزگشایی کند که در اینجا A128CBC-HS256 (اختصار AES-128-CBC و HMAC-SHA256) است.
مثال کامل و قابل اجرای این مطلب را میتوانید از این ریپازیتوری دریافت کنید.