2010-04-05 2 views
22

Как вы обрабатываете запросы ajax, когда пользователь не аутентифицирован?Как вы обрабатываете запросы ajax, когда пользователь не аутентифицирован?

Кто-то входит на страницу, оставляет место на час, возвращает, добавляет комментарий на страницу, которая идет через huhu ajax, используя jQuery ($.post). Поскольку он не аутентифицирован, метод возвращает результат RedirectToRoute (перенаправляется на страницу входа). Что ты делаешь с этим? Как вы справляетесь с этим на стороне клиента и как вы обрабатываете его в контроллере?

+0

Не будет ли проведен его сеанс после 20 минут бездействия? Ваше приложение перенаправляется на другую страницу? – Raja

+0

@Raja: состояние входа в систему истекает через некоторое время, и я спрашиваю, как справиться с этой ситуацией :) Да, он перенаправляется на страницу входа. – LukLed

ответ

16

EDIT:

я писал выше ответ уже давно, и теперь я считаю, что отправка 403 не является правильным способом пойти. 403 имеет несколько иной смысл, и его просто не следует использовать. Это исправляется атрибут с использованием 401. Он отличается только в дополнительной context.HttpContext.Response.End() Http401Result и другой код HTTP:

public class OptionalAuthorizeAttribute : AuthorizeAttribute 
{ 
    private class Http401Result : ActionResult 
    { 
     public override void ExecuteResult(ControllerContext context) 
     { 
      // Set the response code to 401. 
      context.HttpContext.Response.StatusCode = 401; 
      context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue); 
      context.HttpContext.Response.End(); 
     } 
    } 

    private readonly bool _authorize; 

    public OptionalAuthorizeAttribute() 
    { 
     _authorize = true; 
    } 

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced. 
    public OptionalAuthorizeAttribute(bool authorize) 
    { 
     _authorize = authorize; 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     //When authorize parameter is set to false, not authorization should be performed. 
     if (!_authorize) 
      return true; 

     var result = base.AuthorizeCore(httpContext); 

     return result; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      //Ajax request doesn't return to login page, it just returns 401 error. 
      filterContext.Result = new Http401Result(); 
     } 
     else 
      base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

OLD ОТВЕТ:

Хотя мне нравятся идеи, размещенные в других ответах (которые я имел идея о более раннем), мне нужны образцы кода. Вот они:

Измененный атрибут авторизовать:

public class OptionalAuthorizeAttribute : AuthorizeAttribute 
{ 
    private class Http403Result : ActionResult 
    { 
     public override void ExecuteResult(ControllerContext context) 
     { 
      // Set the response code to 403. 
      context.HttpContext.Response.StatusCode = 403; 
      context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue); 
     } 
    } 

    private readonly bool _authorize; 

    public OptionalAuthorizeAttribute() 
    { 
     _authorize = true; 
    } 

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced. 
    public OptionalAuthorizeAttribute(bool authorize) 
    { 
     _authorize = authorize; 
    } 

    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     //When authorize parameter is set to false, not authorization should be performed. 
     if (!_authorize) 
      return true; 

     var result = base.AuthorizeCore(httpContext); 

     return result; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) 
     { 
      //Ajax request doesn't return to login page, it just returns 403 error. 
      filterContext.Result = new Http403Result(); 
     } 
     else 
      base.HandleUnauthorizedRequest(filterContext); 
    } 
} 

HandleUnauthorizedRequest переопределяется, поэтому он возвращает Http403Result при использовании Ajax. Http403Result изменяет StatusCode на 403 и возвращает сообщение пользователю в ответ. В атрибуте есть дополнительная логика (параметр authorize), потому что я включаю [Authorize] в базовом контроллере и отключите его на некоторых страницах.

Другая важная часть - глобальная обработка этого ответа на стороне клиента. Это то, что я поместил в Site.Master:

<script type="text/javascript"> 
    $(document).ready(
     function() { 
      $("body").ajaxError(
       function(e,request) { 
        if (request.status == 403) { 
         alert(request.responseText); 
         window.location = '/Logout'; 
        } 
       } 
      ); 
     } 
    ); 
</script> 

I место глобальный обработчик ошибок Ajax и когда-либо $.post терпит неудачу с ошибкой 403 ответное сообщение будет предупрежден и пользователь перенаправляется на страницу выхода из системы. Теперь мне не нужно обрабатывать ошибку в каждом запросе $.post, потому что он обрабатывается глобально.

Почему 403, а не 401? 401 обрабатывается внутри с помощью инфраструктуры MVC (поэтому перенаправление на страницу входа выполняется после неудачной авторизации).

Что вы думаете об этом?

+0

Забавно, что Microsoft не предоставил обработчик по умолчанию, подобный этому для запросов ajax. – Cherian

+0

@Cherian: Microsoft сильно продвигает jQuery с ASP.NET, так что это не так странно. Microsoft AJAX все еще здесь, потому что многие люди привыкли к этому с помощью ASP.NET Webforms, но я считаю, что jQuery является предпочтительным способом. – LukLed

+0

Я не это имел в виду. jQuery все еще есть способ. я имел в виду, почему у них не было «AjaxAuthorize (return =« json »), который переопределяет их обработку 401 и дает нам, вероятно, {success:« false », status = 401} – Cherian

4

Идея, с которой я столкнулся, когда сотрудник спросил, как с ней справиться, это - создать атрибут AuthorizeAjax. Он может опросить и проверить, что Request.IsAjaxRequest() и, если запрос не аутентифицирован, возвращает конкретный объект ошибки JSON. Возможно, вы могли бы просто переопределить атрибут AuthorizeAttribute по умолчанию и вызвать его, если это неавторизованный запрос AJAX, поэтому вам не нужно беспокоиться о том, следует ли отмечать действия контроллера с помощью [Авторизовать] или [AuthorizeAjax].

На стороне клиента все ваши страницы должны быть оснащены, чтобы иметь дело с возвращенной ошибкой, но эта логика может быть разделена.

+0

Как вы делитесь этим? Как выглядит ваш атрибут AuthorizeAjax? – LukLed

+0

Я не могу поделиться этим, но ASP.Net MVC является открытым исходным кодом, поэтому вы можете взглянуть на то, как они реализовали его для идей. Что касается совместного использования на стороне клиента, вы можете связать центральную функцию (диалог или предупреждение или что-то еще) с событием ошибки AJAX (http://docs.jquery.com/Ajax_Events). – 48klocs

2

Я бы предложил создать свой собственный AuthorizeAttribute, и если запрос является запросом Ajax, бросьте HttpException (401/403). А также переключится на использование jQuery's Ajax Method.

Предполагая, что вы внедрили страницы ошибок, и они вернут правильный код состояния, обратный вызов error будет выполнен вместо обратного вызова success. Это произойдет из-за кода ответа.

0

Самое простое и чистое решение, которое я нашел для этого, - зарегистрировать обратный вызов с событием jQuery.ajaxSuccess() и проверить заголовок ответа «X-AspNetMvc-Version».

Каждый запрос JQuery Ajax в мое приложение обрабатывается Mvc так, если заголовок отсутствует, я знаю, что мой запрос был перенаправлен на страницу входа в систему, и я просто перезагрузите страницу для редиректа верхнего уровня:

$(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) { 
    // if request returns non MVC page reload because this means the user 
    // session has expired 
    var mvcHeaderName = "X-AspNetMvc-Version"; 
    var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName); 

    if (!mvcHeaderValue) { 
     location.reload(); 
    } 
}); 

Перезагрузка страницы может привести к некоторым ошибкам Javascript (в зависимости от того, что вы делаете с ответом Ajax), но в большинстве случаев, когда отладка отсутствует, пользователь никогда их не увидит.

Если вы не хотите использовать встроенный заголовок, я уверен, что вы можете легко добавить пользовательский и следовать одному шаблону.

0

Вот решение, которое я использую. Это мертво просто, если немного грубой силы. Мне нравится, потому что я ленив, и я не хочу думать о специальных атрибутах методов действий, и я не хочу писать обработчики ошибок ajax, если мне не нужно (хотя нет причин, чтобы клиентский скрипт не мог обнаружить код статуса 403 и сделать что-то удобное для пользователя).

Внесение этого в Global.axax обнаруживает любой неаутентифицированный запрос ajax и просто возвращает 403 без содержимого. Это предотвращает перенаправление неавторизованных вызовов ajax в форму входа, когда используется проверка подлинности форм.

protected void Application_AuthenticateRequest(object sender, EventArgs e) 
    { 
     // Prevent Ajax requests from being returned the login form when not authenticated 
     // (eg. after authentication timeout). 
     if ((Request.Headers["X-Requested-With"] != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest") 
      || 
      (Request["X-Requested-With"] != null && Request["X-Requested-With"] == "XMLHttpRequest")) 
     { 
      if (!Request.IsAuthenticated) 
      { 
       Response.Clear(); 
       Response.StatusCode = 403; 
       Response.Flush(); 
       Response.End(); 
      } 
     } 
    } 
0

Вы можете обнаружить запрос Ajax и отправить 401, так и на стороне клиента, вы можете даже показать АЯКС диалог с входом в строке, после чего вы можете «продолжить» неисправный запрос Ajax и сделать вашу работу приложений и пользователь чувствовать так как тайм-аут сеанса никогда не происходил. См. this answer.

Смежные вопросы