AJAX和FormsAuthentication,如何prevent FormsAuthentication重写HTTP 401?重写、FormsAuthentication、AJAX、prevent

2023-09-10 15:56:46 作者:沃维什莫·拿莫帅

在配置FormsAuthentication,当无身份验证cookie,或用过期的一个受保护的页面,ASP.NET发出HTTP 401未经授权,则FormsAuthentication模块拦截请求月底前响应用户访问,和一个应用程序改变一个HTTP 302发现,设置一个HTTP头:为了给用户代理重定向到登录页面的位置/路径/ loginurl,那么浏览器进入该网页,并获取登录页面,这是不受保护的,得到一个HTTP 200 OK。

In one application configured with FormsAuthentication, when a user access without the auth cookie or with an outdated one to a protected page, ASP.NET issue a HTTP 401 Unauthorized, then the FormsAuthentication module intercepts this response before the request end, and change it for a HTTP 302 Found, setting a HTTP header "Location: /path/loginurl" in order to redirect the user agent to the login page, then the browser goes to that page and retrieves the login page, that is not protected, getting an HTTP 200 OK.

这是一个非常好的主意的确,当AJAX没有被考虑。

That was a very good idea indeed, when AJAX was not being considered.

现在我在我的应用程序的URL返回JSON数据,它需要进行身份验证的用户。一切都运行良好,存在的问题是,如果身份验证cookie过期,当我的客户端code调用服务器时,它会得到一个HTTP 200登录页面的HTML,而不是HTTP 401未经授权(因为解释$确定p $ pviously)。然后,我的客户端试图解析登录页面的HTML作为JSON和失败。

Now I have a url in my application that returns JSON data and it needs the user to be authenticated. Everything works well, the problems is that if the auth cookie expires, when my client side code call the server it will get a HTTP 200 OK with the html of the login page, instead a HTTP 401 Unauthorized (because the explained previously). Then my client side is trying to parse the login page html as json, and failing.

接下来的问题是:如何应对来自客户端过期的认证?什么是最优雅的解决方案来应对这种情况呢?我需要知道什么时候该调用成功与否,我想使用HTTP语义做的事。

The question then is : How to cope with an expired authentication from client side? What is the most elegant solution to cope with this situation? I need to know when the call has been successful or not, and I would like to do it using the HTTP semantic.

是否有可能在一个安全的跨浏览器的方式,从客户端读取自定义HTTP头? 有没有办法告诉FormsAuthenticationModule不执行重定向如果请求是一个AJAX请求? 有没有一种方法以同样的方式,你可以重写HTTP请求方法使用HTTP头重写HTTP状态?

Is it possible to read custom HTTP Headers from client side in a safe cross browser way? Is there a way to tell the FormsAuthenticationModule to not perform redirections if the request is an AJAX request? Is there a way to override the HTTP status using a HTTP header in the same way you can override the HTTP request method?

我需要的窗体身份验证,我想避免重写模块或写我自己的窗体身份验证模块。

I need the Forms authentication, and I would like to avoid rewrite that module or write my own form authentication module.

问候。

推荐答案

我有同样的问题,而不得不使用自定义的MVC属性。你可以很容易适应这个网络表单的工作,你可以覆盖基本页面的页面中的授权,如果您的所有网页的一些基本页面继承(MVC中的全局属性可以让同样的事情 - 覆盖OnAuthorization方法在所有控制器/动作应用程序)

I had the same problem, and had to use custom attribute in MVC. You can easy adapt this to work in web forms, you could override authorization of your pages in base page if all your pages inherit from some base page (global attribute in MVC allows the same thing - to override OnAuthorization method for all controllers/actions in 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();或者您的请求将被重定向到登录(我失去了我的一些,因为这头发)。

Note that you need to call HttpContext.Response.End(); or your request will be redirected to login (I lost some of my hair because of this).

在客户端,我用jQuery的ajaxError方式:

On client side, I used jQuery ajaxError method:

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请求重发:

This showed login in iframe over current page (looking like user was redirected but you can make it different), and when login was success, this popup was closed, and original ajax request resent:

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

这可以让你的用户能够在会话,而不会失去他们的任何工作或输入在上所示形式的数据到期登录。

This allows your users to login when session expires without losing any of their work or data typed in last shown form.