API کار با کوکیها نیز در ASP.NET Core نسبت به نگارشهای دیگر تغییریافتهاست که در ادامه این موارد را بررسی خواهیم کرد. همچنین با کمک مطلب «
تغییرات رمزنگاری اطلاعات در NET Core.» یک تامین کنندهی سفارشی کوکیهای رمزنگاری شده را نیز ایجاد میکنیم.
خلاصهای از روشهای کار با کوکیها در ASP.NET Core
ایجاد یک کوکی جدید using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Core1RtmEmptyTest.Controllers
{
public class TestCookiesController : Controller
{
public IActionResult Index()
{
this.Response.Cookies.Append("key", "value", new CookieOptions
{
HttpOnly = true,
Path = this.Request.PathBase.HasValue ? this.Request.PathBase.ToString() : "/",
Secure = this.Request.IsHttps
});
return Content("OK!");
}
}
}
کوکی جدید را میتوان توسط متد Append مجموعهی کوکیها، به Response اضافه کرد:
همانطور که در تصویر نیز مشخص است، طول عمر این کوکی، به سشن تنظیم شدهاست و با پایان سشن جاری مرورگر (بسته شدن کل مرورگر)، این کوکی نیز غیرمعتبر شده و به صورت خودکار حذف خواهد شد. برای تعیین عمر دقیق یک کوکی میتوان از خاصیت Expires شیء CookieOptions که در مثال فوق مقدار دهی نشدهاست، استفاده کرد؛ مانند:
Expires = DateTimeOffset.UtcNow.AddDays(2)
خواندن محتویات کوکی ذخیره شده
پس از ثبت کوکی در Response، خواندن آن در Request بعدی به شکل زیر است:
var value = this.Request.Cookies["key"];
در این حالت اگر کلید درخواستی در مجموعهی کوکیها یافت نشد، نال بازگشت داده میشود.
شیء this.Request.Cookies از نوع IRequestCookieCollection است:
public interface IRequestCookieCollection : IEnumerable<KeyValuePair<string, string>>, IEnumerable
{
string this[string key] { get; }
ICollection<string> Keys { get; }
bool ContainsKey(string key);
bool TryGetValue(string key, out string value);
}
و همانطور که ملاحظه میکنید، برای دریافت مقدار یک کوکی یا میتوان از indexer آن مانند مثال فوق و یا از متد TryGetValue استفاده کرد.
در مستندات آن عنوان شدهاست که در حالت استفادهی از indexer، درصورت یافت نشدن کلید، string.Empty بازگشت داده میشود (که آزمایشات null را نمایش میدهند). اما در حالت استفادهی از TryGetValue بر اساس خروجی bool آن دقیقا میتوان مشخص کرد که آیا این کوکی وجود داشتهاست یا خیر.
در اینجا همچنین متد ContainsKey نیز جهت بررسی وجود یک کلید، در مجموعهی کلیدها نیز پیش بینی شدهاست.
بنابراین بهتر است جهت یافتن مقادیر کوکیها از روش ذیل استفاده کرد:
string cookieValue;
if (this.Request.Cookies.TryGetValue(key, out cookieValue))
{
// TODO: use the cookieValue
}
else
{
// this cookie doesn't exist.
}
حذف کوکیهای موجود
در اینجا متد Delete نیز پیش بینی شدهاست که باید بر روی کوکیهای Response فراخوانی شود:
this.Response.Cookies.Delete("key");
کار آن افزودن یک کوکی دیگر با همین کلید، اما منقضی شدهاست؛ تا مرورگر را مجبور به حذف آن کند. در اینجا در صورت نیاز میتوان به عنوان پارامتر دوم، CookieOptions این کوکی جدید را نیز تنظیم کرد.
همانطور که در تصویر نیز مشخص است، در صورت عدم تنظیم CookieOptions، این کوکی جدید اضافه شده، دارای تاریخ انقضای 1970 است که سبب خواهد شد تا توسط مرورگر، غیرمعتبر درنظر گرفته شده و حذف شود.
طراحی یک تامین کنندهی کوکیهای امن
پس از آشنایی با مقدمات کوکیها و همچنین «
بررسی تغییرات رمزنگاری اطلاعات در NET Core.»، اکنون میتوان یک تامین کنندهی کوکیهای رمزنگاری شده را برای ASP.NET Core به نحو ذیل طراحی کرد:
public interface ISecureCookiesProvider
{
void Add(HttpContext context, string token, string value);
bool Contains(HttpContext context, string token);
string GetValue(HttpContext context, string token);
void Remove(HttpContext context, string token);
}
public class SecureCookiesProvider : ISecureCookiesProvider
{
private readonly IProtectionProvider _protectionProvider;
public SecureCookiesProvider(IProtectionProvider protectionProvider)
{
_protectionProvider = protectionProvider;
}
public void Add(HttpContext context, string token, string value)
{
value = _protectionProvider.Encrypt(value);
context.Response.Cookies.Append(token, value, getCookieOptions(context));
}
public bool Contains(HttpContext context, string token)
{
return context.Request.Cookies.ContainsKey(token);
}
public string GetValue(HttpContext context, string token)
{
string cookieValue;
if (!context.Request.Cookies.TryGetValue(token, out cookieValue))
{
return null;
}
return _protectionProvider.Decrypt(cookieValue);
}
public void Remove(HttpContext context, string token)
{
if (context.Request.Cookies.ContainsKey(token))
{
context.Response.Cookies.Delete(token);
}
}
/// <summary>
/// Expires at the end of the browser's session.
/// </summary>
private CookieOptions getCookieOptions(HttpContext context)
{
return new CookieOptions
{
HttpOnly = true,
Path = context.Request.PathBase.HasValue ? context.Request.PathBase.ToString() : "/",
Secure = context.Request.IsHttps
};
}
}
- نکاتی را که در ابتدای مطلب در مورد ثبت و خواندن و حذف کوکیها مطالعه کردید، به این کلاس اضافه شدهاند. با این تغییر که پیش از ذخیرهی مقدار کوکی، این مقدار رمزنگاری میشود و همچنین پس از خواندن آن، رمزگشایی خواهد شد.
- در این تامین کنندهی کوکیهای امن، IProtectionProvider تزریقی به سازندهی کلاس را در مطلب «
تغییرات رمزنگاری اطلاعات در NET Core.» پیشتر ملاحظه کردهاید.
- در اینجا برای ثبت سرویس جدید، تنظیمات ابتدایی برنامه چنین شکلی را پیدا میکنند و پس از آن میتوان سرویس ISecureCookiesProvider را به کنترلرهای برنامه تزریق و استفاده کرد:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IProtectionProvider, ProtectionProvider>();
services.TryAddSingleton<ISecureCookiesProvider, SecureCookiesProvider>();
- چون در کلاس SecureCookiesProvider، خاصیت Expires تنظیم نشدهاست، طول عمر این کوکیها محدود است به مدت زمان باز بودن مرورگر. بنابراین در صورت نیاز این مورد را تغییر دهید.