Обработка тайм-аута сеанса в вызовах ajax
Я делаю вызов ajax с использованием jquery для действия контроллера asp.net mvc:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetWeek(string startDay)
{
var daysOfWeek = CompanyUtility.GetWeek(User.Company.Id, startDay);
return Json(daysOfWeek);
}
Когда время сеанса заканчивается, этот вызов не будет выполнен, так как объект пользователя будет сохранен в сеансе. Я создал специальный атрибут авторизации, чтобы проверить, был ли сеанс потерян и перенаправлен на страницу входа. Это отлично работает для запросов страниц, однако оно не работает для запросов ajax, поскольку вы не можете перенаправлять запрос ajax:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAjaxRequest())
{//validate http request.
if (!httpContext.Request.IsAuthenticated
|| httpContext.Session["User"] == null)
{
FormsAuthentication.SignOut();
httpContext.Response.Redirect("~/?returnurl=" + httpContext.Request.Url.ToString());
return false;
}
}
return true;
}
}
Я прочитал в другом потоке, что, когда пользователь не аутентифицирован и вы делаете запрос ajax, вы должны установить код состояния 401 (неавторизованный), а затем проверить его в js и перенаправить на страницу входа в систему. Однако я не могу заставить это работать:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAjaxRequest() && (!Request.IsAuthenticated || User == null))
{
filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
}
else
{
base.OnActionExecuting(filterContext);
}
}
В принципе, он установит его на 401, но затем он продолжит действие контроллера и выбросит объект ref, не установленный в экземпляр объектной ошибки, который затем возвращает ошибку 500 обратно на клиентскую сторону js, Если я изменю свой собственный атрибут Authorize, чтобы проверить аякс-запросы, а также вернуть false для тех, которые не прошли проверку подлинности, это делает запрос ajax возвращать мою страницу входа, которая, очевидно, не работает.
Как мне заставить это работать?
Ответы
Ответ 1
Вы можете написать собственный атрибут [Authorize]
, который вернет JSON вместо того, чтобы выбрасывать исключение 401 в случае несанкционированного доступа, что позволит скриптам клиента обрабатывать сценарий изящно:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = new
{
// put whatever data you want which will be sent
// to the client
message = "sorry, but you were logged out"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
затем украсьте свой контроллер/действия с ним и на клиенте:
$.get('@Url.Action("SomeAction")', function (result) {
if (result.message) {
alert(result.message);
} else {
// do whatever you were doing before with the results
}
});
Ответ 2
Я бы не изменил JsonRequestBehavior на AllowGet. Вместо этого я предлагаю:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
OnAuthorizationHelp(filterContext);
}
internal void OnAuthorizationHelp(AuthorizationContext filterContext)
{
if (filterContext.Result is HttpUnauthorizedResult)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.End();
}
}
}
}
и добавьте глобальный обработчик ошибок js ajax:
$(document).ajaxError(function (xhr, props) {
if (props.status === 401) {
location.reload();
}
}
Ответ 3
Даже если это хорошо прошло, я думаю, что это самый короткий и самый сладкий ответ, если вы используете .NET 4.5. Маленькое свойство, называемое SuppressFormsAuthenticationRedirect, которое было добавлено. Установите значение "Истина", и он не будет выполнять перенаправление 302 на страницу входа.
http://msdn.microsoft.com/en-us/library/system.web.httpresponse.suppressformsauthenticationredirect.aspx
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AjaxAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// returns a 401 already
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// we simply have to tell mvc not to redirect to login page
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
}
}
Предполагая, что вы планируете обрабатывать обратный вызов с ошибкой/ошибкой ajax, в котором вы получите неавторизованный 401.
Ответ 4
У меня была аналогичная проблема, и я нашел this
Вместо того, чтобы возвращать какой-либо JSON, перед отправкой ответа, заставьте ASP.NET вернуть код 401. В Global.asax
:
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(Context);
if (context.Request.IsAjaxRequest() && context.Response.StatusCode == 302)
{
Context.Response.Clear();
Context.Response.Write("**custom error message**");
Context.Response.StatusCode = 401;
}
}
Затем вы можете позволить клиенту справиться с этим в JavaScript/jQuery или в том, что вы используете
Ответ 5
На главной странице добавьте этот jquery script ------------
<script type="text/javascript">
$.ajaxSetup({
statusCode: {
403: function () {
window.location.reload();
}
}
});
OR
$.ajaxSetup({
error: function (x, e) {
if (x.status == 403) {
window.location.reload();
}
}
});
</script>
Добавьте файл cs с именем TraceFilter в ваш проект и напишите класс printd TraceFilterAttribute, наследующий ActionFilterAttribute.
Добавьте класс TraceFilterAttribute в FilterConfig.cs, доступный в папке App_Start вашего проекта, написав ниже строку.
filters.Add(новый TraceFilterAttribute());
Переопределить метод OnActionExecuting() в классе TraceFilterAttribute. Это автоматически проверит сеанс и, если найдет нулевой сеанс, вызовет script, доступный на главной странице, и с их помощью вы можете перейти на страницу выбора.
[AttributeUsage(AttributeTargets.All)]
public sealed class TraceFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext != null)
{
HttpSessionStateBase objHttpSessionStateBase = filterContext.HttpContext.Session;
var userSession = objHttpSessionStateBase["etenetID"];
if (((userSession == null) && (!objHttpSessionStateBase.IsNewSession)) || (objHttpSessionStateBase.IsNewSession))
{
objHttpSessionStateBase.RemoveAll();
objHttpSessionStateBase.Clear();
objHttpSessionStateBase.Abandon();
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.Result = new JsonResult { Data = "LogOut" };
}
else
{
filterContext.Result = new RedirectResult("~/Admin/GoToLogin");
}
}
}
}
}
Ответ 6
вот как я обрабатываю это так просто в моей пользовательской авторизации, я проверяю, отключен ли сеанс, и обрабатывайте это как неавторизованное с логическим, чтобы проверить, действительно ли он аутентифицирован, но не авторизован (для перенаправления на un- авторизованной странице) или не аутентифицируется из-за таймаута сеанса (перенаправление на вход)
private bool ispha_LoggedIn = false;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
ispha_LoggedIn = false;
var session = httpContext.Session;
bool authorize = false;
if (httpContext.Session["authenticationInfo"] == null)
{
return authorize;
}
using (OrchtechHR_MVCEntities db = new OrchtechHR_MVCEntities())
{
UserAuthenticationController UM = new UserAuthenticationController();
foreach (var roles in userAssignedRoles)
{
authorize = UM.IsUserInRole(httpContext.User.Identity.Name, roles);
if (authorize)
{
return authorize;
}
}
}
ispha_LoggedIn = true;
return authorize;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (ispha_LoggedIn==false)
{
filterContext.Result = new RedirectResult("~/UserAuthentication/LogIn");
}
else
{
filterContext.Result = new RedirectResult("~/Dashboard/UnAuthorized");
}
}
Надеюсь, если это поможет кому-то и, пожалуйста, если есть комментарии, которые его ценят, чтобы их узнать.
Ответ 7
Возможно, вы захотите попробовать HttpException и поймать его в своем javascript.
throw new HttpException(401, "Auth Failed")
Ответ 8
при вызове ajax, если сеанс истек, верните что-то вроде этого
<script>
$(function(){
location.reload();
});
</script>