امنیت مرورگر به طور پیشفرض same-origin policy (خط مشی با مبدا یکسان) را اعمال میکند. این محدودیت به این معنی است که اگر یک صفحه وب میخواهد به دادههای صفحه دیگری دسترسی داشته باشد، هر دو صفحه باید منشأ یکسانی داشته باشند. این کار کمی سختگیرانه است زیرا مبدا یک صفحه فقط به دامنه اشاره نمی کند. آنها باید شِما (http/https)، هاست (www) و پورت یکسانی داشته باشند. بنابراین site.com و site.com:9000 منشاء های متفاوتی در نظر گرفته می شوند. برنامه ها اغلب نیاز به اشتراک گذاری داده ها با سایت های دیگر دارند. به عنوان مثال، اگر سرویس هایی مانند API های وب را در معرض دسترسی مشتریان قرار داده اید. در نظر داشته باشید، کاری که شما نمی خواهید انجام دهید این است که به منابع خود دسترسی گسترده ای به استفاده کنندگان از این سرویس ها بدهید، اما باید به نحوی درجه سختی same-origin policy (خط مشی با مبدا یکسان) را کاهش دهید. اینجاست که cross-origin resource sharing (CORS) (به اشتراک گذاری منابع با مبدا متفاوت) مطرح می شود. این مقوله به سرور اجازه می دهد تا منابع را با سایت هایی که به آنها اعتماد دارد به اشتراک بگذارد.
همانطور که در ادامه خواهید دید، CORS در درخواستهای cross-origin که به آنها اجازه میدهید یا رد میکنید، انعطافپذیری زیادی به شما میدهد. من چند تغییر در Solution ایجاد کرده ام تا به شما نشان دهم CORS چگونه کار می کند. پروژه جدیدی به نام Tutorial.AspNetSecurity.WebApi وجود دارد که با استفاده از قالب Asp.Net Core web API ایجاد کرده ام. پروژه با یک Controller با مقادیر پیش فرض ایجاد می شود و من یک controller برای دوره های دروس (courses) نیز اضافه کرده ام.
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace Tutorial.AspNetSecurity.WebApi.Controllers
{
[Route("api/[controller]")]
public class CoursesController : Controller
{
// GET api/courses
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "Computer Science", "Biology", "Anthropology" };
}
}
}
این Controller یک متد ساده برای دریافت لیستی از نام دوره ها دارد. وب سایت Roux Academy MVC در این پروژه برای دریافت اطلاعات دوره، وب API ایجاد شده را فراخوانی می کند. ما دادهها را در ویوی Classifications که قبلاً اضافه کردیم نمایش خواهیم داد. من قبلاً کتابخانه های jQuery را در پروژه قرار داده ام.
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@model IEnumerable<string>
@{
ViewData["Title"] = "Student Classifications";
Layout = "_ContentLayout";
}
<fieldset>
<h1>Student Classifications</h1>
<br />
@foreach (var classification in Model)
{
<p>
@classification<br>
</p>
}
<br />
<h1>Course Names</h1>
<div id="CourseNames">
</div>
</fieldset>
<script>
$(function () {
$.ajax({
url: "http://localhost:29909/api/courses",
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
$('#CourseNames').empty();
$.each(result, function (index, value) {
$('#CourseNames').append('<p>' + value + '</p>');
});
}
});
})
</script>
از آنجایی که ما یک فراخوانی ajax ایجاد میکنیم، آدرس مربوط به WEB API ما که در اینجا مشاهده میکنید از شماره پورت دیگری استفاده میکند که این درخواست را به یک درخواست cross-origin تبدیل میکند. بیایید ببینیم وقتی هر دو پروژه را اجرا می کنیم چه اتفاقی می افتد. در مرورگر developer tools را باز کنید و به صفحه /Student/Classifications بروید. اگر به پایین صفحه توجه کنید، می بینید که نام دوره ها نمایش داده نمی شود و یک خطا در پنجره کنسول وجود دارد.
درخواست ajax ما به دلیل خط مشی مبدا یکسان (same-origin policy) مرورگر ناموفق بود. همچنین سرور Web API با هدر HTTP ای که نشان دهد به کدام مبدأ اجازه می دهد، پاسخی صادر نکرد. این همان چیزی است که هدر Access-Control-Allow-Origin برای آن استفاده می شود. این بخشی از استاندارد هسته اصلی است.
این خطا همچنین به درخواست قبل از شروع برنامه اشاره می کند، بدین معنی که، کاری که این روال انجام می دهد این است که قبل از ارسال درخواست واقعی، بررسی می کند که آیا دسترسی مجاز است یا خیر.
ما باید این مشکل را با فعال کردن درخواستهای cross-origin در وب API حل کنیم. در کلاس startup از پروژه web API، متد configure services را پیدا کنیم.
و ما سرویس مربوط به Cors را ثبت می کنیم.
services.AddCors(options =>
{
options.AddPolicy("RouxAcademy",
builder =>
builder.WithOrigins("http://localhost:40992")
.WithMethods("GET")
.AllowAnyHeader());
});
در این بخش از کد ما میتوانیم یک نام برای خط مشی(policy) اختصاص دهیم، که در اینجا ما نام خط مشی را RouxAcademy قرار دادیم. سپس مشخص کردیم که به چه مبدا ی اجازه استفاده از سرویس API را می دهد که در اینجا URL سایت MVC ما است. در مرحله بعد تصمیم خواهیم گرفت که کدام روش HTTP مجاز است. و ما می توانیم این کار را با فراخوانی متد WithMethods انجام دهیم. اینجا فقط متد های GET را انتخاب می کنیم اما شما می توانید همه متد ها را مجاز کنید. و در نهایت، اجازه هر هدری را می دهیم.
سپس به سراغ متد Configure می رویم و قبل از اینکه MVC را اضافه کنیم، پیش می رویم و میان افزار Cors خود را به pipeline برنامه اضافه می کنیم. و از نام policy که در بالا تعریف کردیم استفاده می کنیم که Roux Academy است.
app.UseCors("RouxAcademy");
اگر بالا برای خط مشی نامی را تعریف نکرده بودیم، میتوانیم از شیئ سازنده Cors به این شکل استفاده کنیم.
app.UseCors(buidler =>
buidler.AllowAnyHeader());
و ما میتوانیم مبدا ها، هدرها و متد های خود را اضافه کنیم. من ترجیح می دهم از روش اول یعنی همان روش خط مشی نامگذاری شده استفاده کنم زیرا می توانم چندین خط مشی را در یک پروژه تعریف کنم و از آنها مجدداً با اشاره به نام استفاده کنم. ما اکنون این خط مشی تعریف شده را به صورت کلی برای هر درخواستی، برای وب API خود اعمال کرده ایم. بیایید دوباره سایت را اجرا کنیم.
و دوباره، به صفحه Classifications می رویم. این بار درخواست ajax به Web API به درستی کار می کند. و نام دوره ها نمایش داده می شود.
اگر بخواهیم خط مشی مربوط به Cors را در سطح کل برنامه مانند آنچه انجام دادهایم، اعمال نکنیم، و بخواهیم به صورت انتخابیتر انجام دهیم، چه باید بکنیم. Cors این امکان را در سطح Controller و Action نیز ارائه می دهد.
ابتدا، فراخوانی متدی را که خط مشی مربوط به Cors را در سطح برنامه در pipeline مربوط به برنامه ثبت می کند، به صورت کامنت در می آوریم.
//app.UseCors("RouxAcademy");
در کنترلر CoursesController.cs، یک ویژگی Enable Cors را به بالای تعریف کلاس مربوط به کنترلرهمراه با نام تعریف شده برای آن اضافه می کنیم.
[EnableCors("RouxAcademy")]
کاری که این عمل ما انجام می دهد این است که فقط به درخواست های cross-origin اجازه می دهد تا به متد های این کنترلر خاص دسترسی داشته باشند. سایت Roux Academy به کنترلرهای دیگر مانند ValuesController.cs دسترسی نخواهد داشت. همچنین میتوانیم با جزئیات بیشتری عمل کنیم و آن را به یک اکشن اضافه کنیم، , همانطور باز میتوانیم آن را برای یک Controller یا یک Action با استفاده از ویژگی Disable Cors غیرفعال کنیم. بیایید ادامه دهیم و این کار را برای ValuesController.cs انجام دهیم تا از دسترسی هر سایتی به آن جلوگیری کنیم.
[DisableCors]
خطمشیهای (policies) Cors روشی انعطافپذیر برای فعال کردن درخواستهای cross-origin و محافظت از منابع شما در برابر دسترسی های غیرقابل اعتماد هستند.