ASP.NET Core의 리소스 기반 권한 부여

권한 부여 방법은 리소스에 따라 달라집니다. 예를 들어 문서의 작성자만 문서를 업데이트할 수 있는 권한이 부여됩니다. 따라서 권한 부여 평가가 발생하기 전에 데이터 저장소에서 문서를 검색해야 합니다.

특성 평가는 데이터를 바인딩하기 전과 문서를 로드하는 페이지 처리기 또는 동작을 실행하기 전에 발생합니다. 이러한 이유로 [Authorize] 특성을 가진 선언적 권한 부여로는 충분하지 않습니다. 대신 명령적 권한 부여라고 하는 스타일인 사용자 지정 권한 부여 방법을 호출할 수 있습니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법). 다운로드 예제는 영역을 테스트하기 위한 기초적인 앱을 제공합니다.

권한 부여로 보호되는 사용자 데이터를 사용하여 ASP.NET Core 앱 만들기에는 리소스 기반 권한 부여를 사용하는 샘플 앱이 포함되어 있습니다.

명령적 권한 부여 사용

권한 부여는 서비스로 IAuthorizationService 구현되며 애플리케이션 시작 시 서비스 컬렉션에 등록됩니다. 이 서비스는 종속성 주입을 통해 페이지 처리기 또는 작업에 사용할 수 있습니다.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService에는 두 개의 AuthorizeAsync 메서드 오버로드가 있습니다. 하나는 리소스 및 정책 이름을 수락하고 다른 하나는 리소스 및 평가할 요구 사항 목록을 수락합니다.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

다음 예제에서는 보안할 리소스가 사용자 지정 Document 개체에 로드됩니다. 현재 사용자가 제공된 문서를 편집할 수 있는지 여부를 확인하기 위해 AuthorizeAsync 오버로드가 호출됩니다. 사용자 지정 "EditPolicy" 권한 부여 정책이 결정에 고려됩니다. 권한 부여 정책 만들기에 대한 자세한 내용은 사용자 지정 정책 기반 권한 부여를 참조하세요.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

리소스 기반 처리기 작성

리소스 기반 권한 부여에 대한 처리기를 작성하는 것은 일반 요구 사항 처리기를 작성하는 것과 크게 다르지 않습니다. 사용자 지정 요구 사항 클래스를 만들고 요구 사항 처리기 클래스를 구현합니다. 요구 사항 클래스를 만드는 자세한 내용은 요구 사항을 참조하세요.

처리기 클래스는 요구 사항과 리소스 유형을 모두 지정합니다. 예를 들어 SameAuthorRequirementDocument 리소스를 활용하는 처리기는 다음과 같습니다.

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

앞의 예제에서 SameAuthorRequirement가 제네릭 SpecificAuthorRequirement 클래스의 특수한 경우라고 상상해 보세요. SpecificAuthorRequirement 클래스(표시되지 않음)에는 작성자의 이름을 나타내는 Name 속성이 포함되어 있습니다. Name 속성을 현재 사용자로 설정할 수 있습니다.

Program.cs에서 요구 사항 및 처리기를 등록합니다.

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();

운영 요구 사항

CRUD(만들기, 읽기, 업데이트, 삭제) 작업의 결과에 따라 결정을 내리는 경우 도우미 클래스를 OperationAuthorizationRequirement 사용합니다. 이 클래스를 사용하면 각 작업 형식에 대한 개별 클래스 대신 단일 처리기를 작성할 수 있습니다. 이를 사용하려면 몇 가지 작업 이름을 제공합니다.

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

처리기는 OperationAuthorizationRequirement 요구 사항 및 Document 리소스를 사용하여 다음과 같이 구현됩니다.

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

위의 처리기는 리소스, 사용자의 ID 및 요구 사항의 Name 속성을 사용하여 작업의 유효성을 검사합니다.

작업 리소스 처리기를 사용하는 챌린지 및 금지

이 섹션에서는 챌린지 및 금지 작업 결과가 처리되는 방식과 챌린지 및 금지가 어떻게 다른지 보여 줍니다.

작업 리소스 처리기를 호출하려면 페이지 처리기 또는 작업에서 AuthorizeAsync를 호출할 때 작업을 지정합니다. 다음 예제에서는 인증된 사용자가 제공된 문서를 볼 수 있는지 여부를 확인합니다.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

권한 부여가 성공하면 문서를 보기 위한 페이지가 반환됩니다. 권한 부여가 실패하지만 사용자가 인증된 경우 ForbidResult를 반환하면 인증 미들웨어에 인증이 실패했음을 알립니다. 인증을 수행해야 하는 경우 ChallengeResult가 반환됩니다. 대화형 브라우저 클라이언트의 경우 사용자를 로그인 페이지로 리디렉션하는 것이 적절할 수 있습니다.

권한 부여 방법은 리소스에 따라 달라집니다. 예를 들어 문서의 작성자만 문서를 업데이트할 수 있는 권한이 부여됩니다. 따라서 권한 부여 평가가 발생하기 전에 데이터 저장소에서 문서를 검색해야 합니다.

특성 평가는 데이터를 바인딩하기 전과 문서를 로드하는 페이지 처리기 또는 동작을 실행하기 전에 발생합니다. 이러한 이유로 [Authorize] 특성을 가진 선언적 권한 부여로는 충분하지 않습니다. 대신 명령적 권한 부여라고 하는 스타일인 사용자 지정 권한 부여 방법을 호출할 수 있습니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법). 다운로드 예제는 영역을 테스트하기 위한 기초적인 앱을 제공합니다.

권한 부여로 보호되는 사용자 데이터를 사용하여 ASP.NET Core 앱 만들기에는 리소스 기반 권한 부여를 사용하는 샘플 앱이 포함되어 있습니다.

명령적 권한 부여 사용

권한 부여는 서비스로 IAuthorizationService 구현되며 클래스 내의 서비스 컬렉션에 Startup 등록됩니다. 이 서비스는 종속성 주입을 통해 페이지 처리기 또는 작업에 사용할 수 있습니다.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService에는 두 개의 AuthorizeAsync 메서드 오버로드가 있습니다. 하나는 리소스 및 정책 이름을 수락하고 다른 하나는 리소스 및 평가할 요구 사항 목록을 수락합니다.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

다음 예제에서는 보안할 리소스가 사용자 지정 Document 개체에 로드됩니다. 현재 사용자가 제공된 문서를 편집할 수 있는지 여부를 확인하기 위해 AuthorizeAsync 오버로드가 호출됩니다. 사용자 지정 "EditPolicy" 권한 부여 정책이 결정에 고려됩니다. 권한 부여 정책 만들기에 대한 자세한 내용은 사용자 지정 정책 기반 권한 부여를 참조하세요.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

리소스 기반 처리기 작성

리소스 기반 권한 부여에 대한 처리기를 작성하는 것은 일반 요구 사항 처리기를 작성하는 것과 크게 다르지 않습니다. 사용자 지정 요구 사항 클래스를 만들고 요구 사항 처리기 클래스를 구현합니다. 요구 사항 클래스를 만드는 자세한 내용은 요구 사항을 참조하세요.

처리기 클래스는 요구 사항과 리소스 유형을 모두 지정합니다. 예를 들어 SameAuthorRequirementDocument 리소스를 활용하는 처리기는 다음과 같습니다.

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

앞의 예제에서 SameAuthorRequirement가 제네릭 SpecificAuthorRequirement 클래스의 특수한 경우라고 상상해 보세요. SpecificAuthorRequirement 클래스(표시되지 않음)에는 작성자의 이름을 나타내는 Name 속성이 포함되어 있습니다. Name 속성을 현재 사용자로 설정할 수 있습니다.

Startup.ConfigureServices에서 요구 사항 및 처리기를 등록합니다.

services.AddControllersWithViews();
services.AddRazorPages();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

운영 요구 사항

CRUD(만들기, 읽기, 업데이트, 삭제) 작업의 결과에 따라 결정을 내리는 경우 도우미 클래스를 OperationAuthorizationRequirement 사용합니다. 이 클래스를 사용하면 각 작업 형식에 대한 개별 클래스 대신 단일 처리기를 작성할 수 있습니다. 이를 사용하려면 몇 가지 작업 이름을 제공합니다.

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

처리기는 OperationAuthorizationRequirement 요구 사항 및 Document 리소스를 사용하여 다음과 같이 구현됩니다.

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

위의 처리기는 리소스, 사용자의 ID 및 요구 사항의 Name 속성을 사용하여 작업의 유효성을 검사합니다.

작업 리소스 처리기를 사용하는 챌린지 및 금지

이 섹션에서는 챌린지 및 금지 작업 결과가 처리되는 방식과 챌린지 및 금지가 어떻게 다른지 보여 줍니다.

작업 리소스 처리기를 호출하려면 페이지 처리기 또는 작업에서 AuthorizeAsync를 호출할 때 작업을 지정합니다. 다음 예제에서는 인증된 사용자가 제공된 문서를 볼 수 있는지 여부를 확인합니다.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

권한 부여가 성공하면 문서를 보기 위한 페이지가 반환됩니다. 권한 부여가 실패하지만 사용자가 인증된 경우 ForbidResult를 반환하면 인증 미들웨어에 인증이 실패했음을 알립니다. 인증을 수행해야 하는 경우 ChallengeResult가 반환됩니다. 대화형 브라우저 클라이언트의 경우 사용자를 로그인 페이지로 리디렉션하는 것이 적절할 수 있습니다.

권한 부여 방법은 리소스에 따라 달라집니다. 예를 들어 문서의 작성자만 문서를 업데이트할 수 있는 권한이 부여됩니다. 따라서 권한 부여 평가가 발생하기 전에 데이터 저장소에서 문서를 검색해야 합니다.

특성 평가는 데이터를 바인딩하기 전과 문서를 로드하는 페이지 처리기 또는 동작을 실행하기 전에 발생합니다. 이러한 이유로 [Authorize] 특성을 가진 선언적 권한 부여로는 충분하지 않습니다. 대신 명령적 권한 부여라고 하는 스타일인 사용자 지정 권한 부여 방법을 호출할 수 있습니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법). 다운로드 예제는 영역을 테스트하기 위한 기초적인 앱을 제공합니다.

권한 부여로 보호되는 사용자 데이터를 사용하여 ASP.NET Core 앱 만들기에는 리소스 기반 권한 부여를 사용하는 샘플 앱이 포함되어 있습니다.

명령적 권한 부여 사용

권한 부여는 서비스로 IAuthorizationService 구현되며 클래스 내의 서비스 컬렉션에 Startup 등록됩니다. 이 서비스는 종속성 주입을 통해 페이지 처리기 또는 작업에 사용할 수 있습니다.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService에는 두 개의 AuthorizeAsync 메서드 오버로드가 있습니다. 하나는 리소스 및 정책 이름을 수락하고 다른 하나는 리소스 및 평가할 요구 사항 목록을 수락합니다.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

다음 예제에서는 보안할 리소스가 사용자 지정 Document 개체에 로드됩니다. 현재 사용자가 제공된 문서를 편집할 수 있는지 여부를 확인하기 위해 AuthorizeAsync 오버로드가 호출됩니다. 사용자 지정 "EditPolicy" 권한 부여 정책이 결정에 고려됩니다. 권한 부여 정책 만들기에 대한 자세한 내용은 사용자 지정 정책 기반 권한 부여를 참조하세요.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

리소스 기반 처리기 작성

리소스 기반 권한 부여에 대한 처리기를 작성하는 것은 일반 요구 사항 처리기를 작성하는 것과 크게 다르지 않습니다. 사용자 지정 요구 사항 클래스를 만들고 요구 사항 처리기 클래스를 구현합니다. 요구 사항 클래스를 만드는 자세한 내용은 요구 사항을 참조하세요.

처리기 클래스는 요구 사항과 리소스 유형을 모두 지정합니다. 예를 들어 SameAuthorRequirementDocument 리소스를 활용하는 처리기는 다음과 같습니다.

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

앞의 예제에서 SameAuthorRequirement가 제네릭 SpecificAuthorRequirement 클래스의 특수한 경우라고 상상해 보세요. SpecificAuthorRequirement 클래스(표시되지 않음)에는 작성자의 이름을 나타내는 Name 속성이 포함되어 있습니다. Name 속성을 현재 사용자로 설정할 수 있습니다.

Startup.ConfigureServices에서 요구 사항 및 처리기를 등록합니다.

services.AddMvc();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

운영 요구 사항

CRUD(만들기, 읽기, 업데이트, 삭제) 작업의 결과에 따라 결정을 내리는 경우 도우미 클래스를 OperationAuthorizationRequirement 사용합니다. 이 클래스를 사용하면 각 작업 형식에 대한 개별 클래스 대신 단일 처리기를 작성할 수 있습니다. 이를 사용하려면 몇 가지 작업 이름을 제공합니다.

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

처리기는 OperationAuthorizationRequirement 요구 사항 및 Document 리소스를 사용하여 다음과 같이 구현됩니다.

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

위의 처리기는 리소스, 사용자의 ID 및 요구 사항의 Name 속성을 사용하여 작업의 유효성을 검사합니다.

작업 리소스 처리기를 사용하는 챌린지 및 금지

이 섹션에서는 챌린지 및 금지 작업 결과가 처리되는 방식과 챌린지 및 금지가 어떻게 다른지 보여 줍니다.

작업 리소스 처리기를 호출하려면 페이지 처리기 또는 작업에서 AuthorizeAsync를 호출할 때 작업을 지정합니다. 다음 예제에서는 인증된 사용자가 제공된 문서를 볼 수 있는지 여부를 확인합니다.

참고 항목

다음 코드 샘플에서는 인증이 실행되고 User 속성을 설정했다고 가정합니다.

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

권한 부여가 성공하면 문서를 보기 위한 페이지가 반환됩니다. 권한 부여가 실패하지만 사용자가 인증된 경우 ForbidResult를 반환하면 인증 미들웨어에 인증이 실패했음을 알립니다. 인증을 수행해야 하는 경우 ChallengeResult가 반환됩니다. 대화형 브라우저 클라이언트의 경우 사용자를 로그인 페이지로 리디렉션하는 것이 적절할 수 있습니다.