programing

양식 인증 : 로그인 페이지로 리디렉션 비활성화

nasanasas 2020. 11. 4. 08:04
반응형

양식 인증 : 로그인 페이지로 리디렉션 비활성화


ASP.NET 폼 인증을 사용하는 응용 프로그램이 있습니다. 대부분의 경우 훌륭하게 작동하지만 .ashx 파일을 통해 간단한 API에 대한 지원을 추가하려고합니다. ashx 파일에 선택적 인증이 있기를 원합니다 (즉, 인증 헤더를 제공하지 않으면 익명으로 작동합니다). 하지만 무엇을하는지에 따라 특정 조건에서 인증을 요구하고 싶습니다.

필요한 인증이 제공되지 않은 경우 상태 코드 401로 응답하는 것이 간단한 문제라고 생각했지만 Forms Authentcation 모듈이이를 가로 채고 대신 로그인 페이지로의 리디렉션으로 응답하는 것 같습니다. ProcessRequest방법이 다음과 같다 면 내 말은 :

public void ProcessRequest(HttpContext context)
{
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
}

그런 다음 예상대로 클라이언트에서 401 오류 코드를받는 대신 실제로 로그인 페이지에 대한 302 리디렉션이 발생합니다.

일반적인 HTTP 트래픽의 경우 어떻게 유용한 지 알 수 있지만 API 페이지의 경우 401이 수정되지 않은 상태로 이동하여 클라이언트 측 호출자가 대신 프로그래밍 방식으로 응답 할 수 있도록합니다.

그렇게 할 수있는 방법이 있습니까?


ASP.NET 4.5는 Boolean HttpResponse.SuppressFormsAuthenticationRedirect속성을 추가 했습니다.

public void ProcessRequest(HttpContext context)
{
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
    Response.SuppressFormsAuthenticationRedirect = true;
}

약간의 조사 끝에 FormsAuthenticationModuleHttpApplicationContext.EndRequest이벤트에 대한 처리기를 추가하는 것처럼 보입니다 . 핸들러에서 401 상태 코드를 확인하고 기본적으로 Response.Redirect(loginUrl)대신 수행합니다. 내가 말할 수있는 한,을 사용하려는 경우이 동작을 재정의 할 방법이 없습니다 FormsAuthenticationModule.

내가 그것을 해결하는 방법은 다음 FormsAuthenticationModule과 같이 web.config에서 비활성화하는 것 입니다.

<authentication mode="None" />

그리고 Application_AuthenticateEvent나 자신 을 구현합니다 .

void Application_AuthenticateRequest(object sender, EventArgs e)
{
    if (Context.User == null)
    {
        var oldTicket = ExtractTicketFromCookie(Context, FormsAuthentication.FormsCookieName);
        if (oldTicket != null && !oldTicket.Expired)
        {
            var ticket = oldTicket;
            if (FormsAuthentication.SlidingExpiration)
            {
                ticket = FormsAuthentication.RenewTicketIfOld(oldTicket);
                if (ticket == null)
                    return;
            }

            Context.User = new GenericPrincipal(new FormsIdentity(ticket), new string[0]);
            if (ticket != oldTicket)
            {
                // update the cookie since we've refreshed the ticket
                string cookieValue = FormsAuthentication.Encrypt(ticket);
                var cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName] ??
                             new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath };

                if (ticket.IsPersistent)
                    cookie.Expires = ticket.Expiration;
                cookie.Value = cookieValue;
                cookie.Secure = FormsAuthentication.RequireSSL;
                cookie.HttpOnly = true;
                if (FormsAuthentication.CookieDomain != null)
                    cookie.Domain = FormsAuthentication.CookieDomain;
                Context.Response.Cookies.Remove(cookie.Name);
                Context.Response.Cookies.Add(cookie);
            }
        }
    }
}

private static FormsAuthenticationTicket ExtractTicketFromCookie(HttpContext context, string name)
{
    FormsAuthenticationTicket ticket = null;
    string encryptedTicket = null;

    var cookie = context.Request.Cookies[name];
    if (cookie != null)
    {
        encryptedTicket = cookie.Value;
    }

    if (!string.IsNullOrEmpty(encryptedTicket))
    {
        try
        {
            ticket = FormsAuthentication.Decrypt(encryptedTicket);
        }
        catch
        {
            context.Request.Cookies.Remove(name);
        }

        if (ticket != null && !ticket.Expired)
        {
            return ticket;
        }

        // if the ticket is expired then remove it
        context.Request.Cookies.Remove(name);
        return null;
    }
}

실제로는 그것보다 약간 더 복잡하지만 기본적으로 FormsAuthenticationModulein reflector 의 구현을보고 코드를 얻었습니다 . 내 구현은 FormsAuthenticationModule401로 응답하면 아무 작업도 수행하지 않는다는 점 에서 기본 제공과 다릅니다 . 로그인 페이지로 전혀 리디렉션되지 않습니다. 그것이 요구 사항이된다면, 자동 리디렉션 등을 비활성화하기 위해 컨텍스트에 항목을 넣을 수 있습니다.


이것이 모든 사람에게 작동하는지 확실하지 않지만 IIS7에서는 상태 코드와 설명을 설정 한 후 Response.End ()를 호출 할 수 있습니다. 이렇게하면 # & $ ^ # @ *! FormsAuthenticationModule은 리디렉션을 수행하지 않습니다.

public void ProcessRequest(HttpContext context) {
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
    Response.End();
}

To build on zacharydl's answer slightly, I used this to solve my woes. On every request, at the beginning, if it's AJAX, immediately suppress the behavior.

protected void Application_BeginRequest()
{
    HttpRequestBase request = new HttpRequestWrapper(Context.Request);
    if (request.IsAjaxRequest())
    {
        Context.Response.SuppressFormsAuthenticationRedirect = true;
    }
}

I don't know how that Response.End() worked for you. I tried it with no joy, then looked at MSDN for Response.End(): 'stops execution of the page, and raises the EndRequest event'.

For what it's worth my hack was:

_response.StatusCode = 401;
_context.Items["401Override"] = true;
_response.End();

Then in Global.cs add an EndRequest handler (which will get called after Authentication HTTPModule):

protected void Application_EndRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Items["401Override"] != null)
    {
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.StatusCode = 401;
    }
}

I know there is already an answer with tick but while trying to solve similar problem I found this (http://blog.inedo.com/2010/10/12/http-418-im-a-teapot-finally-a-%e2%80%9clegitimate%e2%80%9d-use/) as an alternative.

Basically you return your own HTTP status code (e.g. 418) in your code. In my case a WCF data service.

throw new DataServiceException(418, "401 Unauthorized");

Then use a HTTP module to handle it at the EndRequest event to rewrite the code back to 401.

HttpApplication app = (HttpApplication)sender;
if (app.Context.Response.StatusCode == 418)
{
    app.Context.Response.StatusCode = 401;
}

The browser / client will receive the correct content and status code, it works great for me :)

If you are interested to learn more about HTTP status code 418 see this question & answer.


what you've found out is correct about the forms auth intercepting the 401 and doing a redirect but we also can do that to reverse that.

Basically what you need is an http module to intercept the 302 redirect to the login page and reverse it to a 401.

Steps on doing that is explained in here

The given link is about a WCF service but it is the same in all the forms auth scenarios.

As explained in the above link you need to clear the http headers as well but remember to put the cookie header back to the response if the original response (i.e. before intercepting) contained any cookies.


That's a known issue, and there's a NuGet Package for that and/or the source code available.


You do not set the WWW-Authenticate header in the code you show, so the client cannot do HTTP authentication instead of forms authentication. If this is the case, you should use 403 instead of 401, which will not be intercepted by the FormsAuthenticaitonModule.


Funny hack if you use.NET Framework >= v4.0 but < v4.5. It uses reflection to set value of inaccessible SuppressFormsAuthenticationRedirect property:

// Set property to "true" using reflection
Response
  .GetType()
  .GetProperty("SuppressFormsAuthenticationRedirect")
  .SetValue(Response, true, null);

I had the problem that I wanted to avoid not only the redirect but also the forms authentication itself in order to make a web api work. Entries in web.config with a location tag for the api didn't help. Thus I used SuppressFormAuthenticationRedirect and HttpContext.Current.SkipAuthorization to suppress the authentication in general. In order to identify the sender I used e.g. the UserAgent in the Header but it is of course recommendable to implement further authentification steps, e.g. check against the sending IP or send another key with the request. Below is inserted in the Global.asax.cs.

protected void Application_BeginRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Request.UserAgent == "SECRET-AGENT")
        {
            AppLog.Log("Redirect suppressed");

            HttpApplication context = (HttpApplication)sender;

            context.Response.SuppressFormsAuthenticationRedirect = true;
            HttpContext.Current.SkipAuthorization = true;                
        }
    }

Look inside your Web.config file in configuration\authentication. If there is a forms subelement there with a loginUrl attribute, remove it and try again.

참고URL : https://stackoverflow.com/questions/2839406/forms-authentication-disable-redirect-to-the-login-page

반응형