[C#] 401이 아닌 로그인 페이지를 반환하는 승인되지 않은 webapi 호출

면도기보기에서 호출 된 webapi 메소드가 인증되지 않은 경우 로그인 페이지를 반환하지 않도록 mvc / webapi 프로젝트를 어떻게 구성합니까?

Javascript를 통한 호출을위한 WebApi 컨트롤러가있는 MVC5 애플리케이션입니다.

아래 두 가지 방법

[Route("api/home/LatestProblems")]
[HttpGet()]
public List<vmLatestProblems> LatestProblems()
{
    // Something here
}

[Route("api/home/myLatestProblems")]
[HttpGet()]
[Authorize(Roles = "Member")]
public List<vmLatestProblems> mylatestproblems()
{
   // Something there
}

다음 각도 코드를 통해 호출됩니다.

angular.module('appWorship').controller('latest',
    ['$scope', '$http', function ($scope,$http) {
        var urlBase = baseurl + '/api/home/LatestProblems';
        $http.get(urlBase).success(function (data) {
            $scope.data = data;
        }).error(function (data) {
            console.log(data);
        });
        $http.get(baseurl + '/api/home/mylatestproblems')
          .success(function (data) {
            $scope.data2 = data;
        }).error(function (data) {
            console.log(data);
        });
    }]
);

그래서 로그인하지 않았고 첫 번째 방법은 성공적으로 데이터를 반환합니다. 두 번째 메소드는 로그인 페이지에 해당하는 데이터를 성공 함수로 리턴합니다. 즉, [Authorize]로 스탬프 처리되고 로그인하지 않은 컨트롤러 작업을 요청한 경우 mvc에 표시되는 내용입니다.

401을 무단으로 반환하여 로그인했는지 여부에 따라 사용자에 대해 다른 데이터를 표시 할 수 있기를 원합니다. 이상적으로 사용자가 로그인 한 경우 해당 회원의 특정 데이터를 반환 할 수 있도록 컨트롤러의 사용자 속성에 액세스 할 수 있기를 원합니다.

업데이트 : 아래 제안 중 더 이상 작동하지 않는 것처럼 보이므로 (Identity 또는 WebAPI 변경) github 에서 원시 예제를 작성 하여 문제를 설명해야합니다.



답변

AuthorizeAttribute 구현에는 두 가지가 있으며 웹 API에 맞는 것을 참조하고 있는지 확인해야합니다. 이 System.Web.Http.AuthorizeAttribute 웹 API의 사용되며, System.Web.Mvc.AuthorizeAttribute 전망 컨트롤러에 사용됩니다. Http.AuthorizeAttribute는 인증이 실패 할 경우 401 오류를 반환하고 Mvc.AuthorizeAttribute는 로그인 페이지로 리디렉션됩니다.

2013 년 11 월 26 일 업데이트

따라서 브록 앨런 이 그의 기사에서 지적한 것처럼 MVC 5에서는 상황이 크게 변화 한 것으로 보입니다 . OWIN 파이프 라인이 대신하여 새로운 동작을 소개합니다. 이제 사용자에게 권한이 부여되지 않으면 HTTP 헤더에 다음 정보와 함께 상태 200이 리턴됩니다.

X-Responded-JSON: {"status":401,"headers":{"location":"http:\/\/localhost:59540\/Account\/Login?ReturnUrl=%2Fapi%2FTestBasic"}}

클라이언트 측의 논리를 변경하여 오류 분기에서 401 상태를 찾는 대신 헤더에서이 정보를 확인하여이를 처리하는 방법을 결정할 수 있습니다.

OnAuthorizationHandleUnauthorizedRequest 메서드 의 응답 상태를 설정하여 사용자 지정 AuthorizeAttribute 에서이 동작을 재정의하려고했습니다 .

actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

그러나 이것은 효과가 없었습니다. 새 파이프 라인은이 응답을 나중에 가져 와서 이전과 동일한 응답으로 수정해야합니다. 500 오류 상태로 변경되어 HttpException이 발생하지 않았습니다.

Brock Allen의 솔루션을 테스트했으며 jQuery ajax 호출을 사용할 때 작동했습니다. 그것이 당신을 위해 작동하지 않으면 내 추측은 각도를 사용하고 있기 때문입니다. Fiddler로 테스트를 실행하고 헤더에 다음이 있는지 확인하십시오.

X-Requested-With: XMLHttpRequest

그렇지 않으면 이것이 문제입니다. 나는 각도에 익숙하지 않지만 자신의 헤더 값을 삽입 할 수 있다면 이것을 아약스 요청에 추가하면 아마 작동하기 시작할 것이다.


답변

Brock Allen은 쿠키 인증 및 OWIN을 사용할 때 ajax 호출에 대해 401을 반환하는 방법에 대한 멋진 블로그 게시물을 보유하고 있습니다.
http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

이것을 Startup.Auth.cs 파일의 ConfigureAuth 메소드에 넣습니다.

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
  AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  LoginPath = new PathString("/Account/Login"),
  Provider = new CookieAuthenticationProvider
  {
    OnApplyRedirect = ctx =>
    {
      if (!IsAjaxRequest(ctx.Request))
      {
        ctx.Response.Redirect(ctx.RedirectUri);
      }
    }
  }
});

private static bool IsAjaxRequest(IOwinRequest request)
{
  IReadableStringCollection query = request.Query;
  if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
  {
     return true;
  }
  IHeaderDictionary headers = request.Headers;
  return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}


답변

asp.net MVC 웹 사이트에 asp.net WebApi를 추가하는 경우 일부 요청에 대해 무단으로 응답하려고합니다. 그러나 ASP.NET 인프라가 작동하고 응답 상태 코드를 HttpStatusCode로 설정하려고하면 302가 로그인 페이지로 리디렉션됩니다.

여기에 asp.net ID 및 owin 기반 인증을 사용하는 경우 해당 문제를 해결하는 데 도움이되는 코드가 있습니다.

public void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                if (!IsApiRequest(ctx.Request))
                {
                    ctx.Response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });

    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}


private static bool IsApiRequest(IOwinRequest request)
{
    string apiPath = VirtualPathUtility.ToAbsolute("~/api/");
    return request.Uri.LocalPath.StartsWith(apiPath);
}


답변

OWIN이 WebApi의 401 응답을 항상 로그인 페이지로 리디렉션 할 때와 같은 상황이 발생했습니다. 우리의 웹 API는 Angular의 ajax 호출뿐만 아니라 Mobile, Win Form 호출도 지원합니다. 따라서 요청이 아약스 요청인지 확인하는 솔루션은 실제로 우리의 경우에 따라 정렬되지 않습니다.

Suppress-RedirectwebApi에서 응답이 오는 경우 새로운 헤더 응답을 삽입하는 또 다른 방법을 선택 했습니다. 구현은 핸들러에 있습니다.

public class SuppressRedirectHandler : DelegatingHandler
{
    /// <summary>
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var response = task.Result;
            response.Headers.Add("Suppress-Redirect", "True");
            return response;
        }, cancellationToken);
    }
}

그리고이 핸들러를 글로벌 레벨의 WebApi에 등록하십시오.

config.MessageHandlers.Add(new SuppressRedirectHandler());

따라서 OWIN 시작시 응답 헤더에 다음이 있는지 여부를 확인할 수 있습니다 Suppress-Redirect.

public void Configuration(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        AuthenticationType = DefaultApplicationTypes.ApplicationCookie,
        ExpireTimeSpan = TimeSpan.FromMinutes(48),

        LoginPath = new PathString("/NewAccount/LogOn"),

        Provider = new CookieAuthenticationProvider()
        {
            OnApplyRedirect = ctx =>
            {
                var response = ctx.Response;
                if (!IsApiResponse(ctx.Response))
                {
                    response.Redirect(ctx.RedirectUri);
                }
            }
        }
    });
}

private static bool IsApiResponse(IOwinResponse response)
{
    var responseHeader = response.Headers;

    if (responseHeader == null)
        return false;

    if (!responseHeader.ContainsKey("Suppress-Redirect"))
        return false;

    if (!bool.TryParse(responseHeader["Suppress-Redirect"], out bool suppressRedirect))
        return false;

    return suppressRedirect;
}


답변

이전 버전의 ASP.NET에서는 많은 작업수행해야했습니다. 이 작업 하기 위해 했습니다.

좋은 소식은 ASP.NET 4.5를 사용하고 있기 때문입니다. 새로운 HttpResponse.SuppressFormsAuthenticationRedirect를 사용하여 양식 인증 리디렉션을 비활성화 할 수 있습니다. 속성을 .

에서 Global.asax:

protected void Application_EndRequest(Object sender, EventArgs e)
{
        HttpApplication context = (HttpApplication)sender;
        context.Response.SuppressFormsAuthenticationRedirect = true;
}

편집 : 이 기사를 살펴볼 수도 있습니다. 보다 세련되게하는 Sergey Zwezdin 를 .

관련 코드 스 니펫 및 작성자 설명이 아래에 붙여졌습니다. 코드 및 내레이션의 원저자-Sergey Zwezdin .

먼저 – 현재 HTTP 요청이 AJAX 요청인지 확인합니다. 그렇다면 HTTP 401을 HTTP 302로 바꾸지 말아야합니다.

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.IsAjaxRequest())
            response.SuppressFormsAuthenticationRedirect = true;

        base.HandleUnauthorizedRequest(filterContext);
    }
}

둘째 – 조건을 추가합시다 : 사용자가 인증되면 HTTP 403을 보냅니다. 그렇지 않으면 HTTP 401입니다.

public class ApplicationAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;
        var user = httpContext.User;

        if (request.IsAjaxRequest())
        {
            if (user.Identity.IsAuthenticated == false)
                response.StatusCode = (int)HttpStatusCode.Unauthorized;
            else
                response.StatusCode = (int)HttpStatusCode.Forbidden;

            response.SuppressFormsAuthenticationRedirect = true;
            response.End();
        }

        base.HandleUnauthorizedRequest(filterContext);
    }
}

잘 했어. 이제 표준 AuthorizeAttribute의 모든 사용을이 새로운 필터로 바꿔야합니다. 코드를 간직한 사람에게는 적용되지 않을 수 있습니다. 그러나 나는 다른 방법을 모른다. 당신이 있다면, 코멘트로 가자.

마지막으로, 클라이언트 측에서 HTTP 401/403 처리를 추가하기 위해해야 ​​할 일. 코드 중복을 피하기 위해 jQuery에서 ajaxError를 사용할 수 있습니다.

$(document).ajaxError(function (e, xhr) {
    if (xhr.status == 401)
        window.location = "/Account/Login";
    else if (xhr.status == 403)
        alert("You have no enough permissions to request this resource.");
});

결과 –

  • 사용자가 인증되지 않은 경우 AJAX 호출 후 로그인 페이지로 리디렉션됩니다.
  • 사용자가 인증되었지만 충분한 권한이없는 경우 사용자에게 친숙한 오류 메시지가 표시됩니다.
  • 사용자가 인증되고 충분한 권한이있는 경우 오류가 없으며 평소와 같이 HTTP 요청이 진행됩니다.

답변

프로젝트 Web API내에서 실행중인 경우 메소드 에 적용 할 MVC사용자 정의 AuthorizeAttribute를 작성 해야 API합니다. 내부에서 IsAuthorized override다음 HttpContext과 같이 리디렉션을 방지하기 위해 전류를 가져와야합니다 .

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        if (string.IsNullOrWhiteSpace(Thread.CurrentPrincipal.Identity.Name))
        {
            var response = HttpContext.Current.Response;
            response.SuppressFormsAuthenticationRedirect = true;
            response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            response.End();
        }

        return base.IsAuthorized(actionContext);
    }


답변

Azure Active Directory 통합을 직접 사용하면 CookieAuthentication미들웨어를 사용하는 접근 방식이 효과 가 없었습니다. 나는 다음을 수행해야했다.

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ...
        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            ...
            RedirectToIdentityProvider = async context =>
            {
                if (!context.Request.Accept.Contains("html"))
                {
                    context.HandleResponse();
                }
            },
            ...
        }
    });

요청이 브라우저 자체 (예 : AJAX 호출이 아닌)에서 온 경우 Accept 헤더에는 문자열이 포함됩니다. html 어딘가에 . 클라이언트가 HTML을 수락 할 때만 리디렉션이 유용한 것으로 간주합니다.

내 클라이언트 응용 프로그램은 401을 처리하여 사용자에게 앱에 더 이상 액세스 권한이 없으며 다시 로그인하기 위해 다시로드해야 함을 알려줍니다.