Как вы обрабатываете ajax-запросы, когда пользователь не аутентифицирован? - PullRequest
22 голосов
/ 05 апреля 2010

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

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

Ответы [ 6 ]

16 голосов
/ 07 апреля 2010

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);
    }
}

СТАРЫЙ ОТВЕТ:

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

Изменен атрибут авторизации:

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>

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

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

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

4 голосов
/ 05 апреля 2010

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

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

2 голосов
/ 06 апреля 2010

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

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

0 голосов
/ 06 октября 2011

Вы можете обнаружить ajax-запрос и отправить 401, а на стороне клиента вы даже можете отобразить диалоговое окно ajax с приглашением войти в систему, после чего вы можете «продолжить» ваш неудавшийся ajax-запрос и заставить ваше приложение работать и пользователь никогда не будет ощущать тайм-аут сеанса получилось. Подробнее см. этот ответ .

0 голосов
/ 18 августа 2011

Вот решение, которое я использую. Это очень просто, если немного грубой силы. Мне это нравится, потому что я ленивый, и я не хочу думать о специальных атрибутах методов действия, и я не хочу писать обработчики ошибок 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 голосов
/ 19 мая 2010

Самое простое и чистое решение, которое я нашел для этого, - зарегистрировать обратный вызов с помощью события 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), но в большинстве случаев, когда отладка отключена, пользователь никогда не увидит их.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...