کارکرد SOP
فرض میکنیم از طریق یه مرورگر به وبسایت http://www.yyy.com مراجعه و در اون لاگین میکنیم، و این وبسایت هم شناسهی این لاگین رو به صورت کوکی روی دستگاه ما ذخیره میکنه. تا زمانیکه این کوکی روی دستگاه ما ذخیره شده، هر وقت از این دستگاه و با همون مرورگر به این وبسایت مراجعه کنیم، با هر request این کوکی به این وبسایت ارسال میشه و وبسایت میتونه حساب کاربری ما رو بشناسه.
اما نکته اینجاست که حتی وقتی ما روی این دستگاه و با اون مرورگر مشخص، وبسایتی متفاوت مثل http://www.xxx.com رو هم باز میکنیم، اگه این وبسایت در بخشی از عملیاتش یه request به دامنهی yyy بفرسته، مثلا API اون رو فراخوانی کنه، باز هم مرورگر همهی کوکیهای ذخیره شده توسط وبسایت yyy، از جمله کوکی لاگین ما رو، بدون توجه به مبدا ارسال کننده request که در اینجا xxx هستش، به yyy ارسال میکنه. در واقع مرورگر برای ارسال کوکیها، به مقصد request توجه میکنه و نه ارسالکنندهی request. به این ترتیب وبسایت yyy ممکنه request ارسالی از سمت وبسایت xxx رو ارسال شده از طرف حساب کاربری ما در yyy تشخیص بده و اطلاعات محرمانهی ما رو برگردونه یا دسترسیهایی به این وبسایت بده که نباید.
سیاست مرورگرهای امروزی برای جلوگیری از این مشکل، Same Origin Policy یا "SOP" هستش. به طور کلی این سیاست به این صورت پیاده میشه که اسکریپتهای بارگذاری شده توسط یک «منشاء»، نمیتونن به منابعی مثل API از منشاء متفاوت دسترسی داشته باشن. اما معیار یکسان بودن منشاء چیه؟
وقتی دو منبع دارای "scheme"، "host" و "port" کاملا یکسان باشند، اونوقت دارای منشاء یکسان درنظر گرفته میشن. به نظرم درک سریع موضوع با دیدن سه دستهی زیر با منشاءهای متفاوت امکانپذیر خواهد بود. دوتای اول فقط scheme های متفاوت دارن (http , https)، سه تای بعدی host های متفاوت دارن (example, www.example, myapp.example) و دوتای آخری پورتهای متفاوت (8080 و 80 که مقدار پیشفرضه). بنابرین هیچکدوم از سه دسته منشاء یکسانی ندارن.
حالا میشه متوجه شد که وقتی در حال توسعهی یه برنامه روی سیستم خودمون هستیم، با اینکه "backend" و "frontend" هر دو دارن روی http://localhost اجرا میشن، چرا در حالت پیش فرض frontend نمیتونه API رو از backend فراخوانی کنه. چون در واقع روی پورتهای متفاوتی در حال اجرا هستن، یکی به فرض روی http://localhost:4200 و دیگری روی http://localhost:44353. بنابراین طبق توضیحات بالا منشاء یکسانی ندارن، با اینکه پروتکل (http) و host اونها (localhost) یکسانه.
کارکرد CORS
گاهی لازم میشه که از محدودیتهای اعمال شده بوسیلهی SOP کم کنیم. مثلا یه اپ تحت وب با انگولار نوشتیم و روی دامنهی https://www.aaa.com قرار دادیم که باید API قرار گرفته روی دامنهای با منشاء متفاوت مثل https://api.aaa.com رو فراخوانی کنه. برای این منظور از مفهوم Cross Origin Resource Sharing یا "CORS" استفاده میکنیم. در واقع ما با استفاده از CORS به تعدادی از دامنهها اجازهی عبور از محدودیتهای اعمال شده بوسیلهی SOP رو میدیم. برای این کار، اگه API رو با Asp.Net Core پیاده کرده باشیم، در فایل "Startup.cs" و در تابع "ConfigureServices" خواهیم داشت:
public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("AllowMyOrigin", builder => builder.WithOrigins("https://www.aaa.com").AllowAnyHeader()); }); .... }
در واقع ما یه Policy با اسم دلخواه "AllowMyOrigin" که هر اسم دیگهای هم میتونه باشه تعریف کردیم که میگه به "https://www.aaa.com" اجازهی دسترسی به API رو بده، با اینکه منشاء متفاوتی داره. حالا در همون فایل و در تابع "Configure" این Policy رو اِعمال میکنیم:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { .... app.UseCors("AllowMyOrigin"); ..... }
به این ترتیب، محدودیت SOP برای https://www.aaa.com برداشته میشه.
CORS برای اپ Ionic
میدونیم که SOP عملا برای برنامههایی که روی مرورگر اجرا میشن مهمه و در واقع اپهای native موبایل دچار محدودیت در این زمینه نیستن. برنامههای تحت وب هم که یه دامنهی مشخص دارن و دیدیم که میتونیم این دامنه رو استثنا کنیم. اما اپهای موبایل تولید شده با Ionic با توجه به «هیبریدی» بودنشون چه وضعی دارن؟
میدونیم که اپهای Ionic عملا داخل WebView اجرا میشن. بنابراین نه مثل برنامههای تحت وب تو دامنهی مشخصی قرار دارن که اون رو استثنا کنیم، و نه کاملا native هستن که SOP شاملشون نشه. بر طبق مستندات وبسایت Ionic، برای رفع این مشکل میشه از روش زیر استفاده کرد:
public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("AllowMyOrigin", builder => builder.WithOrigins("capacitor://localhost", // capacitor: ios "http://localhost", // capacitor: android | ionic webview 3.x on cordova: android "ionic://localhost", // ionic webview 3.x on cordova : ios "http://localhost:8080", // ionic webview 2.x on cordova : ios & android ).AllowAnyHeader()); }); .... }
در واقع به جای اینکه یه دامنه خاص رو استثنا کنیم، چهار منشاء بالا رو که هرکدوم یک یا چند حالت رو برای اپهای Ionic پوشش میدن استثنا میکنیم و به این ترتیب محدودیت SOP برای اپهای Ionic برداشته میشه.