AJAX и FormsAuthentication, как предотвратить FormsAuthentication вместо HTTP 401?

В одном приложении, настроенном с помощью FormsAuthentication, когда пользователь обращается без cookie аутентификации или с устаревшим файлом к ​​защищенной странице, ASP.NET выдает HTTP 401 Unauthorized, тогда модуль FormsAuthentication перехватывает этот ответ перед завершением запроса и измените его на HTTP 302 Found, установив HTTP-заголовок «Location: / path / loginurl», чтобы перенаправить пользовательский агент на страницу входа, затем браузер переходит на эту страницу и извлекает страницу входа , который не защищен, получение HTTP 200 OK.

Это была действительно очень хорошая идея, когда AJAX не рассматривался.

Теперь у меня есть URL-адрес в моем приложении, которое возвращает данные JSON, и ему нужно, чтобы пользователь прошел аутентификацию. Все работает хорошо, проблема в том, что если срок действия cookie аутентификации истекает, когда мой клиентский код вызывает сервер, он получит HTTP 200 OK с html страницы входа, а не HTTP 401 Unauthorized (потому что объяснено ранее). Затем моя клиентская сторона пытается проанализировать html страницы входа как json и терпит неудачу.

Тогда возникает вопрос: как справиться с истекшим сроком аутентификации на стороне клиента? Как лучше всего справиться с этой ситуацией? Мне нужно знать, был ли вызов успешным или нет, и я хотел бы сделать это с использованием семантики HTTP.

Можно ли читать пользовательские заголовки HTTP со стороны клиента безопасным кроссбраузерным способом? Есть ли способ запретить FormsAuthenticationModule выполнять перенаправления, если запрос является запросом AJAX? Есть ли способ переопределить статус HTTP с помощью HTTP-заголовка так же, как вы можете переопределить метод HTTP-запроса?

Мне нужна аутентификация с помощью форм, и я бы не хотел переписывать этот модуль или писать мой собственный модуль аутентификации формы.

С уважением.

26
задан vtortola 25 September 2011 в 21:14
поделиться

2 ответа

У меня была та же проблема, и мне пришлось использовать пользовательский атрибут в MVC. Вы можете легко адаптировать это для работы в веб-формах, вы можете переопределить авторизацию ваших страниц на базовой странице, если все ваши страницы наследуются от некоторой базовой страницы (глобальный атрибут в MVC позволяет то же самое - переопределить метод OnAuthorization для всех контроллеров / действий в application)

Вот как выглядит атрибут:

public class AjaxAuthorizationAttribute : FilterAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest()
                && !filterContext.HttpContext.User.Identity.IsAuthenticated
                && (filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0
                || filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).Count() > 0))
            {
                filterContext.HttpContext.SkipAuthorization = true;
                filterContext.HttpContext.Response.Clear();
                filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                filterContext.Result = new HttpUnauthorizedResult("Unauthorized");
                filterContext.Result.ExecuteResult(filterContext.Controller.ControllerContext);
                filterContext.HttpContext.Response.End();
            }
        }
    }

Обратите внимание, что вам нужно вызвать HttpContext.Response.End (); или ваш запрос будет перенаправлен на вход (из-за этого я потерял некоторые волосы).

На стороне клиента я использовал метод jQuery ajaxError:

var lastAjaxCall = { settings: null, jqXHR: null };
var loginUrl = "yourloginurl";

//...
//...

$(document).ready(function(){
    $(document).ajaxError(function (event, jqxhr, settings) {
            if (jqxhr.status == 401) {
                if (loginUrl) {
                    $("body").prepend("<div class='loginoverlay'><div class='full'></div><div class='iframe'><iframe id='login' src='" + loginUrl + "'></iframe></div></div>");
                    $("div.loginoverlay").show();
                    lastAjaxCall.jqXHR = jqxhr;
                    lastAjaxCall.settings = settings;
                }
            }
    }

}

Это показывало вход в iframe через текущую страницу (похоже, что пользователь был перенаправлен, но вы можете сделать его другим), и когда вход был успешным, это всплывающее окно было закрыто, и исходный запрос ajax повторно:

if (lastAjaxCall.settings) {
        $.ajax(lastAjaxCall.settings);
        lastAjaxCall.settings = null;
    }

Это позволяет вашим пользователям входить в систему по окончании сеанса, не теряя при этом ни свою работу, ни данные, набранные в последней показанной форме.

23
ответ дан 28 November 2019 в 07:42
поделиться

Я в значительной степени краду этот ответ из других постов, но идея может заключаться в том, чтобы реализовать перехват HttpModule - перенаправления на страницу входа (инструкции по этой ссылке).

Вы также можете изменить этот пример HttpModule так, чтобы он только перехватывал перенаправление, если запрос был сделан через AJAX, если поведение по умолчанию корректно, когда запрос не сделан через AJAX:

Обнаружить вызов ajax, ASP.net

Итак, что-то вроде:

class AuthRedirectHandler : IHttpModule
{
    #region IHttpModule Members

    public void Dispose()
    {

    }

    public void Init(HttpApplication context)
    {
        context.EndRequest+= new EventHandler(context_EndRequest);
    }


    void context_EndRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        if (app.Response.StatusCode == 302 
            && app.Request.Headers["X-Requested-With"] == "XMLHttpRequest"
            && context.Response.RedirectLocation.ToUpper().Contains("LOGIN.ASPX"))
        {
            app.Response.ClearHeaders();
            app.Response.ClearContent();
            app.Response.StatusCode = 401;
        }
    }

    #endregion
}

Вы также можете убедиться, что перенаправление происходит на вашу фактическую страницу входа, если в вашем приложении есть другие допустимые перенаправления 302 .

Тогда вы просто добавите в свой web.config:

  <httpModules>
    <add name="AuthRedirectHandler" type="SomeNameSpace.AuthRedirectHandler, SomeNameSpace" />
  </httpModules>

В любом случае. Опять же, настоящая оригинальная мысль вошла в этот ответ, я просто собираю различные фрагменты из SO и других частей сети.

5
ответ дан 28 November 2019 в 07:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: