اعتبارسنجی درخواست های http$ با استفاده از یک Interceptor
نویسنده: مجتبی دیناروند
تاریخ: ۱۳۹۴/۰۶/۱۱ ۱۰:۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
[Authorize(Roles = AuthorizeRole.SuperAdministrator)]
public partial class HomeController : Controller
{
[HttpPost]
[AngularValidateAntiForgeryToken]
public virtual JsonResult GetUserInfo()
{
var userInfoViewModel = _applicationUserManager.GetUserInfoById(User.Identity.GetUserId());
return Json(userInfoViewModel);
}
} $scope.getUserInfo = function() {
$http({
method: 'POST',
url: 'Home/GetUserInfo',
headers: $scope.getHeaders()
}).
success(function(data, status, headers, config) {
$scope.userInfo = data;
}).
error(function(data, status, headers, config) {
}).then(function(res) {
});
}
همانطور که میبینید دادههای اولیه کاربر پس از ورود به سیستم، بدون هیچ مشکلی دریافت میشوند.
نکته : زمانیکه status برابر با 200 هست، یعنی درخواست OK میباشد. ( در پیوست ، لیست تمامی کدها قرار داده شده است )
حالا فرض کنید کاربر در صفحه حضور دارد و به هر دلیلی اعتبار حضور کاربر منقضی شده است و حالا پس از مدتی کاربر درخواستی را به سرور ارسال میکند و میخواهد اطلاعات خودش را مشاهده کند.
درخواست کاربر با همان کدهای اولیه ارسال میشود و خروجی اینبار به صورت زیر در خواهد آمد :
همانطور که میبینید وضعیت اینبار نیز OK میباشد، ولی هیچ دادهای از سرور دریافت نشده است. کاربر قطعا در اینجا دچار سردرگمی میشود. چون هیچ چیزی را مشاهده نمیکند و به هیچ مسیر دیگری نیز هدایت نمیشود و هرکار دیگری نیز انجام دهد، پاسخی مشاهده نمیکند.
راه حل چیست ؟
ابتدا باید برای درخواستهای Ajax ایی اعتبارسنجی را اعمال کنیم. برای این کار باید یک Attribute جدید بسازیم. یعنی به این صورت :
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class MyCustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// یعنی اعتبارسنجی نشده است
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.HttpContext.Response.End();
}
base.HandleUnauthorizedRequest(filterContext);
}
} $http({
method: 'POST',
url: 'Home/GetUserInfo',
headers: $scope.getHeaders()
}).
success(function(data, status, headers, config) {
$scope.userInfo = data;
}).
error(function(data, status, headers, config) {
if (status === 401) {
// you are not authorized
}
}).then(function(res) {
}); در console مرورگر نیز خطای زیر رخ میدهد :
POST http://localhost:000000/Administrator/Home/GetUserInfo 401 (Unauthorized)
factory('AuthorizedInterceptor', function ($q) {
return {
response: function(response) {
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status === 401) {
// you are not authorized
}
return $q.reject(rejection);
}
};
})
در خروجی بالا میبینید که کاربر، اعتبارسنجی نشده است و آن را به مسیر ورود هدایت خواهیم کرد.
با Interceptor بالا دیگر نیازی نیست برای هر درخواستی این وضعیت را چک کنیم و به صورت خودکار این چک کردن رخ خواهد داد.
پیوست : http_status_codes.rar
با سلام و احترام
ممنون بابت مقاله خوبتون
ولی این شرط
filterContext.HttpContext.Request.IsAjaxRequest
معنی
یعنی اعتبارسنجی نشده است
را نمیدهد.
شما در متد HandleUnauthorizedRequest هستید و بالطبع اعتبار درخواست رد شده است، کاری که میخواهید انجام دهید، اعتبارسنجی نیست، بلکه اصلاح Response درخواستهای Ajax ای است که Status Code آنها ۲۰۰ است که نباید باشد.
این نوع دقت داشتن و شفافیت لازمه ایجاد یک کد صحیح است، چون الآن و در مرحله ای بالاتر، میتوانیم بگویم که هر Response ای که وارد متد HandleUnauthorizedRequest شده است، اگر Status Code 401 ندارد، Status Code آن برابر ۴۰۱ قرار داده شود. فارغ از Ajax بودن یا نبودن.
و باز در مرحله ای بالاتر اگر در پروژه از Owin استفاده شده باشد، یک Owin Middleware و اگر نه یک ASP.NET Module جای مناسبتری برای نوشتن این چنین کدی است.
از این که IsAjaxRequest همیشه درست کار نمیکند و فقط وقتی درخواست Ajax ای بگوید که Ajax ای است، کار خواهد کرد نیز بگذریم.
موفق و پایدار باشید.
چه شرایطی ممکن است رخ دهد که اشتباه کار کند ؟
اگر این درخواست دستی صادر بشه (یک ربات باشه) یا اگر فایروال خاصی در این میان هدرهای Ajax رو حذف کنه. مورد اول مهم نیست، چون نیازی نیست تا ربات را به صفحهی لاگین هدایت کرد (مرورگر باز شدهای نداره). مورد دوم هم خیلی خیلی بعید هست. یعنی من ندیدم که در عمل فایروالهای شرکتها هدرهای HTTP رو تا این حد دستکاری کنند.