ASP.NET Core에서 리소스 기반 권한 부여Resource-based authorization in ASP.NET Core

권한 부여 전략 액세스 되는 리소스에 따라 다릅니다.Authorization strategy depends upon the resource being accessed. 문서를 작성자 속성이 있는 것이 좋습니다.Consider a document that has an author property. 작성자만 문서를 업데이트할 수 있습니다.Only the author is allowed to update the document. 따라서 문서 권한 부여 평가 발생 하기 전에 데이터 저장소에서 검색 해야 합니다.Consequently, the document must be retrieved from the data store before authorization evaluation can occur.

특성 평가 데이터 바인딩 전에 및 페이지 처리기 또는 문서를 로드 하는 작업을 실행 하기 전에 발생 합니다.Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. 이러한 이유로 사용 하 여 선언적 권한 부여는 [Authorize] 특성 충분 하지 않습니다.For these reasons, declarative authorization with an [Authorize] attribute doesn't suffice. 대신 사용자 지정 권한 부여 메서드를 호출할 수 있습니다—라고 하는 스타일 명령적 권한 부여합니다.Instead, you can invoke a custom authorization method—a style known as imperative authorization.

예제 코드 살펴보기 및 다운로드 (다운로드 방법)View or download sample code (how to download).

권한 부여로 보호 되는 사용자 데이터를 사용 하 여 ASP.NET Core 앱 만들기 리소스 기반 권한 부여를 사용 하는 샘플 앱을 포함 합니다.Create an ASP.NET Core app with user data protected by authorization contains a sample app that uses resource-based authorization.

필수 권한 부여 사용Use imperative authorization

권한 부여로 구현 됩니다는 IAuthorizationService 서비스 및 서비스 컬렉션 내에 등록 되는 Startup 클래스입니다.Authorization is implemented as an IAuthorizationService service and is registered in the service collection within the Startup class. 서비스를 통해 제공 됩니다 종속성 주입 페이지 처리기 또는 작업을 합니다.The service is made available via dependency injection to page handlers or actions.

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

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

IAuthorizationService 에 두 개의 AuthorizeAsync 메서드 오버 로드: 리소스와 정책 이름 및 다른 리소스와 평가 하기 위한 요구 사항 목록을 받고 있는 하나의 수락 합니다.IAuthorizationService has two AuthorizeAsync method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate.

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

다음 예제에서는 리소스 보안을 유지 하도록 사용자 지정에 로드 됩니다 Document 개체입니다.In the following example, the resource to be secured is loaded into a custom Document object. AuthorizeAsync 오버 로드는 현재 사용자가 제공된 문서를 편집할 수 있는지 여부를 확인 하려면 호출 됩니다.An AuthorizeAsync overload is invoked to determine whether the current user is allowed to edit the provided document. 사용자 지정 "EditPolicy" 권한 부여 정책 결정으로 구분 됩니다.A custom "EditPolicy" authorization policy is factored into the decision. 참조 사용자 지정 정책 기반 권한 부여 권한 부여 정책 만들기에 대 한 자세한 내용은 합니다.See Custom policy-based authorization for more on creating authorization policies.

참고

다음 코드 샘플에서는 인증 실행으로 가정 및 집합은 User 속성.The following code samples assume authentication has run and set the User property.

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();
    }
}
[HttpGet]
public async Task<IActionResult> Edit(Guid documentId)
{
    Document document = _documentRepository.Find(documentId);

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

    if (await _authorizationService
        .AuthorizeAsync(User, document, "EditPolicy"))
    {
        return View(document);
    }
    else
    {
        return new ChallengeResult();
    }
}

리소스 기반 처리기를 작성 합니다.Write a resource-based handler

리소스 기반 권한 부여 보다 큰 차이가 없습니다.에 대 한 처리기를 작성 작성 한 일반 요구 사항 처리기합니다.Writing a handler for resource-based authorization isn't much different than writing a plain requirements handler. 사용자 지정 요구 사항 클래스를 만들고 요구 사항 처리기 클래스를 구현 합니다.Create a custom requirement class, and implement a requirement handler class. 요구 사항 클래스를 만드는 방법에 대 한 자세한 내용은 참조 하세요. 요구 사항합니다.For more information on creating a requirement class, see Requirements.

처리기 클래스 요구 사항 및 리소스 종류를 지정합니다.The handler class specifies both the requirement and resource type. 예를 들어 한 처리기를 활용 하는 SameAuthorRequirementDocument 리소스 다음과 같습니다.For example, a handler utilizing a SameAuthorRequirement and a Document resource follows:

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 { }
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);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

앞의 예제는 imagine SameAuthorRequirement 는 보다 일반적인의 특수 SpecificAuthorRequirement 클래스입니다.In the preceding example, imagine that SameAuthorRequirement is a special case of a more generic SpecificAuthorRequirement class. SpecificAuthorRequirement (표시 되지 않음) 하는 클래스를 포함 한 Name 작성자의 이름을 나타내는 속성입니다.The SpecificAuthorRequirement class (not shown) contains a Name property representing the name of the author. Name 현재 사용자에 게 속성을 설정할 수 있습니다.The Name property could be set to the current user.

요구 사항 및 처리기의 등록 Startup.ConfigureServices:Register the requirement and handler in 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>();

운영 요구 사항Operational requirements

CRUD (만들기, 읽기, 업데이트, 삭제) 작업의 결과에 따라 결정을 변경 하려는 경우 사용 합니다 OperationAuthorizationRequirement 도우미 클래스입니다.If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the OperationAuthorizationRequirement helper class. 이 클래스를 사용 하면 각 작업 유형에 대 한 개별 클래스 대신 단일 처리기를 작성할 수 있습니다.This class enables you to write a single handler instead of an individual class for each operation type. 를 사용 하려면 몇 가지 작업 이름을 제공 합니다.To use it, provide some operation names:

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 리소스:The handler is implemented as follows, using an OperationAuthorizationRequirement requirement and a Document resource:

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;
    }
}
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);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

이전 처리기 리소스, 사용자의 id 및 요구 사항의 사용 하는 작업의 유효성을 검사 Name 속성입니다.The preceding handler validates the operation using the resource, the user's identity, and the requirement's Name property.

운영 리소스 처리기를 호출 하려면 작업을 호출할 때 지정 AuthorizeAsync 페이지 처리기에 작업 합니다.To call an operational resource handler, specify the operation when invoking AuthorizeAsync in your page handler or action. 다음 예제에서는 인증된 된 사용자 제공된 문서를 볼 수 있는지 여부를 결정 합니다.The following example determines whether the authenticated user is permitted to view the provided document.

참고

다음 코드 샘플에서는 인증 실행으로 가정 및 집합은 User 속성.The following code samples assume authentication has run and set the User property.

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();
    }
}

권한 부여에 성공 하는 경우 문서를 보기 위한 페이지가 반환 됩니다.If authorization succeeds, the page for viewing the document is returned. 경우 권한 부여 실패 하지만 사용자가 인증 되 면 반환 ForbidResult 권한 부여에 실패 하는 모든 인증 미들웨어에 알립니다.If authorization fails but the user is authenticated, returning ForbidResult informs any authentication middleware that authorization failed. ChallengeResult 인증을 수행 해야 하는 경우 반환 됩니다.A ChallengeResult is returned when authentication must be performed. 대화형 브라우저 클라이언트에 대 한 적절 한 로그인 페이지로 사용자를 리디렉션할 수 있습니다.For interactive browser clients, it may be appropriate to redirect the user to a login page.

[HttpGet]
public async Task<IActionResult> View(Guid documentId)
{
    Document document = _documentRepository.Find(documentId);

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

    if (await _authorizationService
        .AuthorizeAsync(User, document, Operations.Read))
    {
        return View(document);
    }
    else
    {
        return new ChallengeResult();
    }
}

권한 부여에 성공 하는 경우 문서에 대 한 보기 반환 됩니다.If authorization succeeds, the view for the document is returned. 권한 부여에 실패 하면 반환 ChallengeResult 알리고 모든 인증 미들웨어는 권한 부여 실패, 미들웨어는 적절 한 응답을 걸릴 수 있습니다.If authorization fails, returning ChallengeResult informs any authentication middleware that authorization failed, and the middleware can take the appropriate response. 적절 한 응답을 401 또는 403 상태 코드를 반환할 수 있습니다.An appropriate response could be returning a 401 or 403 status code. 대화형 브라우저 클라이언트에 대 한 사용자를 로그인 페이지로 리디렉션 의미할 수 있습니다.For interactive browser clients, it could mean redirecting the user to a login page.