인증 및 권한 부여 실패로 AuthorizeAttribute가 로그인 페이지로 리디렉션되는 이유는 무엇입니까?


265

ASP.NET MVC에서 다음과 AuthorizeAttribute같이 컨트롤러 메소드를 마크 업할 수 있습니다 .

[Authorize(Roles = "CanDeleteTags")]
public void Delete(string tagName)
{
    // ...
}

즉, 현재 로그인 한 사용자가 "CanDeleteTags"역할이 아닌 경우 컨트롤러 메소드가 호출되지 않습니다.

불행히도, 실패에 대해서는을 AuthorizeAttribute리턴 HttpUnauthorizedResult하며, 이는 항상 HTTP 상태 코드 401을 리턴합니다. 이로 인해 로그인 페이지로 경로 재 지정됩니다.

사용자가 로그인하지 않은 경우 완벽하게 이해됩니다. 그러나 사용자가 이미 로그인했지만 필요한 역할이 아닌 경우 로그인 페이지로 다시 보내는 것이 혼란 스럽습니다.

AuthorizeAttribute인증 및 권한 부여 가 혼란스러워 보입니다 .

이것은 ASP.NET MVC에서 약간의 감독처럼 보입니까, 아니면 뭔가 빠졌습니까?

나는 DemandRoleAttribute둘을 분리 하는 것을 요리해야했습니다 . 사용자가 인증되지 않으면 HTTP 401을 반환하여 로그인 페이지로 보냅니다. 사용자가 로그인했지만 필요한 역할이 NotAuthorizedResult아닌 경우 대신 생성 됩니다. 현재 이것은 오류 페이지로 리디렉션됩니다.

분명히 나는 ​​이것을 할 필요가 없었습니까?


10
훌륭한 질문이며 동의합니다. HTTP Not Authorized 상태가되어야합니다.
Pure.Krome

3
나는 당신의 해결책을 좋아합니다, Roger. 당신이하지 않더라도.
Jon Davis

내 로그인 페이지에는 이미 사용자가 인증 된 경우 사용자를 ReturnUrl로 리디렉션하기위한 확인이 있습니다. 그래서 나는 302 리디렉션의 무한 루프를 만들었습니다 : D woot.
juhan_h

1
이것을 확인하십시오 .
Jogi

Roger, 솔루션에 관한 좋은 기사 -red-gate.com/simple-talk/dotnet/asp-net/… 솔루션이이 작업을 깨끗하게 수행 할 수있는 유일한 방법 인 것 같습니다
Craig

답변:


305

처음 개발되었을 때 System.Web.Mvc.AuthorizeAttribute는 올바른 작업을 수행했습니다. HTTP 사양의 이전 버전은 "권한 없음"및 "인증되지 않은"상태 코드 401을 사용했습니다.

원래 사양에서 :

요청에 이미 인증 자격 증명이 포함 된 경우 401 응답은 해당 자격 증명에 대한 인증이 거부되었음을 나타냅니다.

사실, 혼란을 볼 수 있습니다. "인증"을 의미 할 때 "권한 부여"라는 단어가 사용됩니다. 그러나 일상적인 연습에서는 사용자가 인증되었지만 권한이없는 경우 403 Forbidden을 반환하는 것이 더 합리적입니다. 사용자에게 액세스 권한을 부여하는 두 번째 자격 증명 세트가 없을 것 같습니다.

대부분의 운영 체제를 고려하십시오-액세스 권한이없는 파일을 읽으려고하면 로그인 화면이 표시되지 않습니다!

고맙게도, 모호성을 제거하기 위해 HTTP 사양이 업데이트되었습니다 (2014 년 6 월).

"Hyper Text Transport Protocol (HTTP / 1.1) : 인증"(RFC 7235)에서 :

401 (인증되지 않음) 상태 코드는 요청이 대상 리소스에 대한 유효한 인증 자격 증명이 없기 때문에 적용되지 않았 음을 나타냅니다.

"하이퍼 텍스트 전송 프로토콜 (HTTP / 1.1) : 의미 및 컨텐츠"(RFC 7231)에서 :

403 (금지됨) 상태 코드는 서버가 요청을 이해했지만 승인을 거부 함을 나타냅니다.

흥미롭게도 ASP.NET MVC 1이 출시 될 당시 AuthorizeAttribute의 동작은 정확했습니다. 이제 동작이 잘못되었습니다. HTTP / 1.1 사양이 수정되었습니다.

ASP.NET의 로그인 페이지 리디렉션을 변경하지 않고 소스에서 문제를 해결하는 것이 더 쉽습니다. 웹 사이트의 기본 네임 스페이스에 동일한 이름 ( AuthorizeAttribute) 으로 새 속성을 만들 수 있습니다 (매우 중요합니다). 그러면 컴파일러는 MVC의 표준 네임 스페이스 대신 자동으로이를 선택합니다. 물론 그 접근 방식을 취하고 싶다면 항상 속성에 새로운 이름을 지정할 수 있습니다.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

52
+1 아주 좋은 접근법. 작은 제안 : 점검 대신에 filterContext.HttpContext.User.Identity.IsAuthenticated, 당신 은 확인 할 수 있습니다 filterContext.HttpContext.Request.IsAuthenticated. 이것은 null 점검이 내장되어 있습니다. stackoverflow.com/questions/1379566/…
Daniel Liuzzi

> 웹 사이트의 기본 네임 스페이스에 동일한 이름 (AuthorizeAttribute)을 사용하여 새 속성을 만들면 컴파일러가 MVC의 표준 네임 스페이스 대신 자동으로 선택합니다. 이로 인해 오류가 발생합니다. 'Authorize'형식 또는 네임 스페이스를 찾을 수 없습니다 (지시문 또는 어셈블리 참조가 누락 되었습니까?) 둘 다 System.Web.Mvc; 내 사용자 정의 AuthorizeAttribute 클래스의 네임 스페이스가 컨트롤러에서 참조됩니다. 이 내가 사용했다 [MyNamepace.Authorize] 해결하기
stormwild

2
@DePeter 사양은 리디렉션에 대해 아무 말도하지 않으므로 리디렉션이 더 나은 솔루션 인 이유는 무엇입니까? 이것만으로도 해킹없이 아약스 요청을 죽일 수 있습니다.
Adam Tuliper-MSFT

1
그것은 분명히 행동 버그이기 때문에 MS Connect에 기록되어야합니다. 감사.
Tony Wall

BTW, 왜 로그인 페이지로 리디렉션 됩니까? 동일한 요청 내에서 401 코드와 로그인 페이지를 직접 출력하지 않는 이유는 무엇입니까?
SandRock

25

이것을 Login Page_Load 함수에 추가하십시오 :

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

사용자가 리디렉션되었지만 이미 로그인하면 권한이없는 페이지가 표시됩니다. 로그인하지 않으면 로그인 페이지가 표시됩니다.


18
Page_Load는 webforms mojo입니다
Chance

2
@Chance-그런 다음 FormsAuthencation이 호출되도록 설정된 제어기에 대해 기본 ActionMethod에서 수행하십시오.
Pure.Krome

이것은 실제로 실제로 잘 작동하지만 MVC의 if (User.Identity != null && User.Identity.IsAuthenticated) return RedirectToRoute("Unauthorized");경우 Unauthorized 가 정의 된 경로 이름 과 같은 것이어야 합니다.
Moses Machua

리소스를 요청하면 로그인 페이지로 리디렉션 되고 403 페이지로 다시 리디렉션 됩니까? 나에게 나쁜 것 같습니다. 심지어 하나의 리디렉션을 전혀 견딜 수 없습니다. IMO 이건 어쨌든 매우 나빠졌습니다.
SandRock

3
귀하의 솔루션에 따르면, 이미 로그인 한 상태에서 URL을 입력하여 로그인 페이지로 이동하면 인증되지 않은 페이지로 이동합니다. 옳지 않다.
Rajshekar Reddy

4

나는 항상 이것이 의미가 있다고 생각했다. 로그인 한 상태에서 가지고 있지 않은 역할이 필요한 페이지를 누르려고하면 해당 역할이있는 사용자로 로그인하라는 로그인 화면이 나타납니다.

사용자가 이미 인증되었는지 확인하는 논리를 로그인 페이지에 추가 할 수 있습니다. 그들이 왜 다시 부딪 쳤는지 설명하는 친절한 메시지를 추가 할 수 있습니다.


4
대부분의 사람들은 주어진 웹 앱에 대해 하나 이상의 정체성을 갖는 경향이 없다고 생각합니다. 만일 그렇다면, 그들은 현재 나의 ID에 모조가 없다고 생각할만큼 영리합니다. 다른 ID로 다시 로그인하겠습니다.
Roger Lipscombe

로그인 페이지에 무언가를 표시하는 것에 대한 다른 요점은 좋은 것입니다. 감사.
Roger Lipscombe

4

불행히도 ASP.NET 폼 인증의 기본 동작을 다루고 있습니다. 여기에 설명 된 해결 방법이 있습니다 (시도하지 않았습니다).

http://www.codeproject.com/KB/aspnet/Custon401Page.aspx

(MVC에만 국한되지 않음)

대부분의 경우 가장 좋은 해결책은 사용자가 접근하기 전에 무단 리소스에 대한 액세스를 제한하는 것입니다. 이 승인되지 않은 페이지로 연결될 수있는 링크 또는 버튼을 제거 / 회색 처리합니다.

권한이없는 사용자를 리디렉션 할 위치를 지정하기 위해 속성에 추가 매개 변수가있는 것이 좋습니다. 그러나 그 동안 AuthorizeAttribute를 안전망으로 봅니다.


인증을 기반으로 링크를 제거 할 계획입니다 (어딘가에 대한 질문을 보았습니다). 나중에 HtmlHelper 확장 메서드를 코딩 할 것입니다.
Roger Lipscombe

1
여전히 사용자가 URL로 직접 이동하지 못하게해야합니다.이 속성은 모두 관련이 있습니다. Custom 401 솔루션 (글로벌 한 것처럼 보임)에 만족하지 않으므로 RedirectToRouteResult에서 NotAuthorizedResult를 모델링 해 봅니다.
Roger Lipscombe

0

Global.ascx 파일의 Application_EndRequest 핸들러에서이를 시도하십시오.

if (HttpContext.Current.Response.Status.StartsWith("302") && HttpContext.Current.Request.Url.ToString().Contains("/<restricted_path>/"))
{
    HttpContext.Current.Response.ClearContent();
    Response.Redirect("~/AccessDenied.aspx");
}

0

aspnetcore 2.0을 사용하는 경우 다음을 사용하십시오.

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Core
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeApiAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var user = context.HttpContext.User;

            if (!user.Identity.IsAuthenticated)
            {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }
}

0

필자의 경우 문제는 "HTTP 인증에"인증되지 않은 "및"인증되지 않은 "상태 코드 401을 사용하는 것"이었습니다. ShadowChaser가 말했듯이.

이 솔루션은 저에게 효과적입니다.

if (User != null &&  User.Identity.IsAuthenticated && Response.StatusCode == 401)
{
    //Do whatever

    //In my case redirect to error page
    Response.RedirectToRoute("Default", new { controller = "Home", action = "ErrorUnauthorized" });
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.