인증 및 권한 부여 실패로 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.