ASP.NET Core의 정책 기반 권한 부여

역할 기반 권한 부여클레임 기반 권한 부여는 요구 사항, 요구 사항 처리기 및 미리 구성된 정책을 사용합니다. 이러한 구성 요소에서는 코드에서 권한 부여 평가의 식을 지원합니다. 그 결과 더 풍부하고 재사용이 가능하며 테스트가 가능한 권한 부여 구조가 생성됩니다.

권한 부여 정책은 하나 이상의 요구 사항으로 구성됩니다. 앱 파일에서 권한 부여 서비스 구성의 Program.cs 일부로 등록합니다.

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

앞의 예제에서 “AtLeast21” 정책이 생성되었습니다. 요구 사항에 대한 매개 변수로 제공되는 최소 기간의 단일 요구 사항이 있습니다.

IAuthorizationService

권한 부여가 성공했는지 확인하는 기본 서비스는 IAuthorizationService입니다.

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

위의 코드는 IAuthorizationService의 두 메서드를 강조 표시합니다.

IAuthorizationRequirement는 메서드가 없는 표식 서비스이며 권한 부여가 성공했는지 여부를 추적하기 위한 메커니즘입니다.

IAuthorizationHandler는 요구 사항이 충족되는지 확인하는 역할을 합니다.

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

AuthorizationHandlerContext 클래스는 처리기가 요구 사항이 충족되었는지 여부를 표시하는 데 사용하는 것입니다.

 context.Succeed(requirement)

다음 코드는 권한 부여 서비스의 간소화된(그리고 주석으로 주석이 추가된) 기본 구현을 보여 줍니다.

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

다음 코드는 일반적인 권한 부여 서비스 구성을 보여 줍니다.

// Add all of your handlers to DI.
builder.Services.AddSingleton<IAuthorizationHandler, MyHandler1>();
// MyHandler2, ...

builder.Services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

// Configure your policies
builder.Services.AddAuthorization(options =>
      options.AddPolicy("Something",
      policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));

권한 부여를 위해 IAuthorizationService, [Authorize(Policy = "Something")] 또는 RequireAuthorization("Something")을 사용합니다.

MVC 컨트롤러에 정책 적용

Razor Pages를 사용하는 앱의 경우 Razor Pages에 정책 적용 섹션을 참조하세요.

정책 이름과 함께 [Authorize] 특성을 사용하여 컨트롤러에 정책을 적용합니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace AuthorizationPoliciesSample.Controllers;

[Authorize(Policy = "AtLeast21")]
public class AtLeast21Controller : Controller
{
    public IActionResult Index() => View();
}

Razor Pages에 정책 적용

정책 이름과 함께 [Authorize] 특성을 사용하여 Razor Pages에 정책을 적용합니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace AuthorizationPoliciesSample.Pages;

[Authorize(Policy = "AtLeast21")]
public class AtLeast21Model : PageModel { }

정책은 페이지 처리기 수준에서 적용 Razor 할 수 없으며 페이지에 적용해야 합니다.

정책은 또한 권한 부여 규칙을 사용하여 Razor Pages에 적용할 수도 있습니다.

엔드포인트에 정책 적용

정책 이름과 함께 RequireAuthorization을 사용하여 엔드포인트에 정책을 적용합니다. 예를 들어:

app.MapGet("/helloworld", () => "Hello World!")
    .RequireAuthorization("AtLeast21");

요구 사항

권한 부여 요구 사항은 정책이 현재 사용자 주체를 평가하는 데 사용할 수 있는 데이터 매개 변수의 컬렉션입니다. "AtLeast21" 정책에서 요구 사항은 단일 매개 변수(최소 기간)입니다. 요구 사항은 빈 표식 인터페이스인 구현합니다 IAuthorizationRequirement. 매개 변수가 있는 최소 연령 요구 사항은 다음과 같이 구현할 수 있습니다.

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int minimumAge) =>
        MinimumAge = minimumAge;

    public int MinimumAge { get; }
}

권한 부여 정책에 여러 권한 부여 요구 사항이 포함된 경우 정책 평가가 성공하려면 모든 요구 사항이 통과되어야 합니다. 즉, 단일 권한 부여 정책에 추가된 여러 권한 부여 요구 사항은 AND 기준으로 처리됩니다.

참고

요구 사항에는 데이터 또는 속성이 필요하지 않습니다.

권한 부여 처리기

권한 부여 처리기는 요구 사항의 속성에 대한 평가를 담당합니다. 권한 부여 처리기는 제공된 AuthorizationHandlerContext 요구 사항을 평가하여 액세스가 허용되는지 확인합니다.

요구 사항에는 여러 처리기가 있을 수 있습니다. 처리기는 처리해야 하는 요구 사항이 있는 경우 TRequirement 상속AuthorizationHandler<TRequirement>할 수 있습니다. 또는 처리기가 둘 이상의 요구 사항을 처리하기 위해 직접 구현 IAuthorizationHandler 할 수 있습니다.

하나의 요구 사항에 처리기 사용

다음 예제에서는 최소 연령 처리기가 단일 요구 사항을 처리하는 일 대 일 관계를 보여 줍니다.

using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
    {
        var dateOfBirthClaim = context.User.FindFirst(
            c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com");

        if (dateOfBirthClaim is null)
        {
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

위의 코드는 현재 사용자 보안 주체에 알려진 신뢰할 수 있는 발급자가 발급한 생년월일 클레임의 날짜가 있는지 확인합니다. 클레임이 누락된 경우 권한 부여가 발생할 수 없으며, 이 경우 완료된 작업이 반환됩니다. 클레임이 있으면 사용자의 연령이 계산됩니다. 사용자가 요구 사항에 정의된 최소 연령을 충족하는 경우 권한 부여는 성공한 것으로 간주됩니다. 권한 부여가 성공하면 context.Succeed는 충족된 요구 사항을 유일한 매개 변수로 호출합니다.

여러 요구 사항에 처리기 사용

다음 예제에서는 권한 처리기가 세 가지 유형의 요구 사항을 처리할 수 있는 일 대 다 관계를 보여 줍니다.

using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource)
                    || IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission || requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        return Task.CompletedTask;
    }

    private static bool IsOwner(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }

    private static bool IsSponsor(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }
}

앞의 코드는 성공으로 표시되지 않은 요구 사항을 포함하는 속성인 트래버스 PendingRequirements합니다. ReadPermission 요구 사항의 경우 요청된 리소스에 액세스하려면 사용자가 소유자 또는 스폰서여야 합니다. 요청된 리소스에 EditPermissionDeletePermission 액세스하려면 소유자여야 합니다.

처리기 등록

구성 중에 서비스 컬렉션에 처리기를 등록합니다. 예를 들어:

builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

위의 코드에서는 MinimumAgeHandler를 싱글톤으로 등록합니다. 기본 제공 서비스 수명을 사용하여 처리기를 등록할 수 있습니다.

요구 사항과 처리기를 IAuthorizationRequirementIAuthorizationHandler 모두를 구현하는 단일 클래스로 번들로 묶을 수 있습니다. 이 번들링은 처리기와 요구 사항 간의 긴밀한 결합을 생성하며 간단한 요구 사항 및 처리기에만 권장됩니다. 두 인터페이스를 모두 구현하는 클래스를 만들면 요구 사항을 직접 처리할 수 있는 기본 제공 PassThroughAuthorizationHandler 로 인해 DI에 처리기를 등록할 필요가 없습니다.

완전한 자체 포함 클래스의 요구 사항 및 처리기 모두에 대한 좋은 예제 AssertionRequirementAssertionRequirement 클래스를 참조하세요.

처리기는 무엇을 반환해야 합니까?

처리기 예제Handle 메서드는 값을 반환하지 않습니다. 성공 또는 실패 상태는 어떻게 표시하나요?

  • 처리기는 context.Succeed(IAuthorizationRequirement requirement)를 호출하여 성공적으로 유효성이 검사된 요구 사항을 전달함으로써 성공을 나타냅니다.

  • 동일한 요구 사항에 대한 다른 처리기가 성공할 수 있기 때문에 처리기는 일반적으로 오류를 처리할 필요가 없습니다.

  • 다른 요구 사항 처리기가 성공하더라도 실패를 보장하려면 context.Fail을 호출합니다.

처리기에서 context.Succeed 또는 context.Fail을 호출하는 경우 다른 모든 처리기가 계속 호출됩니다. 이렇게 하면 다른 처리기가 요구 사항의 유효성을 성공적으로 검사하거나 실패한 경우에도 로깅과 같은 부작용이 발생할 수 있습니다. 설정 false하면 속성이 InvokeHandlersAfterFailure 호출되면 처리기 실행을 단락합니다 context.Fail . InvokeHandlersAfterFailure는 기본적으로 true로 설정되며, 이 경우 모든 처리기가 호출됩니다.

참고

인증이 실패하더라도 권한 부여 처리기가 호출됩니다.

요구 사항에 대해 여러 처리기를 원하는 이유는 무엇인가요?

OR 기준으로 평가하려는 경우 단일 요구 사항에 대해 여러 처리기를 구현합니다. 예를 들어 Microsoft에는 키 카드로만 열리는 문이 있습니다. 키 카드를 집에 그대로 두면 수신자가 임시 스티커를 인쇄하고 문을 엽니다. 이 시나리오에서는 BuildingEntry라는 단일 요구 사항이 있지만 각 요구 사항을 검사하는 여러 처리기가 있습니다.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

public class BuildingEntryRequirement : IAuthorizationRequirement { }

BadgeEntryHandler.cs

using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(
            c => c.Type == "BadgeId" && c.Issuer == "https://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

TemporaryStickerHandler.cs

using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(
            c => c.Type == "TemporaryBadgeId" && c.Issuer == "https://microsoftsecurity"))
        {
            // Code to check expiration date omitted for brevity.
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

두 처리기가 모두 등록되었는지 확인합니다. 정책이 BuildingEntryRequirement를 평가할 때 두 처리기가 성공하면 정책 평가가 성공합니다.

함수를 사용하여 정책 수행

정책을 이행하는 것이 코드로 표현하기 쉬운 상황이 있을 수 있습니다. RequireAssertion 정책 작성기를 사용하여 정책을 구성할 때 Func<AuthorizationHandlerContext, bool>을 제공할 수 있습니다.

예를 들어 이전 BadgeEntryHandler를 다음과 같이 다시 작성할 수 있습니다.

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context => context.User.HasClaim(c =>
            (c.Type == "BadgeId" || c.Type == "TemporaryBadgeId")
            && c.Issuer == "https://microsoftsecurity")));
});

처리기에서 MVC 요청 컨텍스트에 액세스

HandleRequirementAsync 메서드에는 처리 중인 AuthorizationHandlerContextTRequirement라는 두 매개 변수가 있습니다. MVC 또는 SignalR과 같은 프레임워크는 추가 정보를 전달하기 위해 개체를 AuthorizationHandlerContextResource 속성에 자유롭게 추가할 수 있습니다.

엔드포인트 라우팅을 사용하는 경우 권한 부여는 일반적으로 권한 부여 미들웨어에 의해 처리됩니다. 이 경우 Resource 속성은 HttpContext의 인스턴스입니다. 컨텍스트를 사용하여 현재 엔드포인트에 액세스할 수 있습니다. 이 엔드포인트는 라우팅하는 기본 리소스를 프로브하는 데 사용할 수 있습니다. 예를 들어:

if (context.Resource is HttpContext httpContext)
{
    var endpoint = httpContext.GetEndpoint();
    var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
    ...
}

기존 라우팅을 사용하거나 MVC 권한 부여 필터의 일부로 권한 부여가 발생하는 경우 Resource의 값은 AuthorizationFilterContext 인스턴스입니다. 이 속성은 MVC 및 Razor Pages에서 제공되는 HttpContext, RouteData 및 모든 항목에 대한 액세스를 제공합니다.

속성의 Resource 사용은 프레임워크에 따라 다릅니다. Resource 속성의 정보를 사용하면 권한 부여 정책이 특정 프레임워크로 제한됩니다. 키워드를 Resourceis 사용하여 속성을 캐스팅한 다음 캐스트가 성공했는지 확인하여 코드가 다른 프레임워크에서 실행될 때 충돌 InvalidCastException 하지 않도록 합니다.

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

전역적으로 모든 사용자를 인증하도록 요구

모든 사용자의 인증을 전역으로 요구하는 방법에 대한 자세한 내용은 인증된 사용자 요구를 참조하세요.

역할 기반 권한 부여클레임 기반 권한 부여는 요구 사항, 요구 사항 처리기 및 미리 구성된 정책을 사용합니다. 이러한 구성 요소에서는 코드에서 권한 부여 평가의 식을 지원합니다. 그 결과 더 풍부하고 재사용이 가능하며 테스트가 가능한 권한 부여 구조가 생성됩니다.

권한 부여 정책은 하나 이상의 요구 사항으로 구성됩니다. 정책은 Startup.ConfigureServices 메서드에 권한 부여 서비스 구성의 일부로 등록됩니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}

앞의 예제에서 “AtLeast21” 정책이 생성되었습니다. 요구 사항에 대한 매개 변수로 제공되는 최소 기간의 단일 요구 사항이 있습니다.

IAuthorizationService

권한 부여가 성공했는지 확인하는 기본 서비스는 IAuthorizationService입니다.

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

위의 코드는 IAuthorizationService의 두 메서드를 강조 표시합니다.

IAuthorizationRequirement는 메서드가 없는 표식 서비스이며 권한 부여가 성공했는지 여부를 추적하기 위한 메커니즘입니다.

IAuthorizationHandler는 요구 사항이 충족되는지 확인하는 역할을 합니다.

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

AuthorizationHandlerContext 클래스는 처리기가 요구 사항이 충족되었는지 여부를 표시하는 데 사용하는 것입니다.

 context.Succeed(requirement)

다음 코드는 권한 부여 서비스의 간소화된(그리고 주석으로 주석이 추가된) 기본 구현을 보여 줍니다.

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

다음 코드는 일반적인 ConfigureServices를 보여 줍니다.

public void ConfigureServices(IServiceCollection services)
{
    // Add all of your handlers to DI.
    services.AddSingleton<IAuthorizationHandler, MyHandler1>();
    // MyHandler2, ...

    services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

    // Configure your policies
    services.AddAuthorization(options =>
          options.AddPolicy("Something",
          policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));


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

권한 부여를 위해 IAuthorizationService 또는 [Authorize(Policy = "Something")]을 사용합니다.

MVC 컨트롤러에 정책 적용

Razor Pages를 사용하는 경우 이 문서의 Razor Pages에 정책 적용을 참조하세요.

정책은 정책 이름으로 [Authorize] 특성을 사용하여 컨트롤러에 적용됩니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

Razor Pages에 정책 적용

정책 이름과 함께 [Authorize] 특성을 사용하여 Razor Pages에 정책을 적용합니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

정책은 페이지 처리기 수준에서 적용 Razor 할 수 없으며 페이지에 적용해야 합니다.

정책은 권한 부여 규칙을 사용하여 Razor Pages에 적용할 수도 있습니다.

요구 사항

권한 부여 요구 사항은 정책이 현재 사용자 주체를 평가하는 데 사용할 수 있는 데이터 매개 변수의 컬렉션입니다. "AtLeast21" 정책에서 요구 사항은 단일 매개 변수(최소 기간)입니다. 요구 사항은 빈 표식 인터페이스인 구현합니다 IAuthorizationRequirement. 매개 변수가 있는 최소 연령 요구 사항은 다음과 같이 구현할 수 있습니다.

using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

권한 부여 정책에 여러 권한 부여 요구 사항이 포함된 경우 정책 평가가 성공하려면 모든 요구 사항이 통과되어야 합니다. 즉, 단일 권한 부여 정책에 추가된 여러 권한 부여 요구 사항은 AND 기준으로 처리됩니다.

참고

요구 사항에는 데이터 또는 속성이 필요하지 않습니다.

권한 부여 처리기

권한 부여 처리기는 요구 사항의 속성에 대한 평가를 담당합니다. 권한 부여 처리기는 제공된 AuthorizationHandlerContext 요구 사항을 평가하여 액세스가 허용되는지 확인합니다.

요구 사항에는 여러 처리기가 있을 수 있습니다. 처리기는 처리해야 하는 요구 사항이 있는 경우 TRequirement 상속AuthorizationHandler<TRequirement>할 수 있습니다. 또는 처리기가 둘 이상의 요구 사항 유형을 처리하도록 구현 IAuthorizationHandler 할 수 있습니다.

하나의 요구 사항에 처리기 사용

다음 예제에서는 최소 연령 처리기가 단일 요구 사항을 활용하는 일대일 관계를 보여 줍니다.

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            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;
    }
}

위의 코드는 현재 사용자 보안 주체에 알려진 신뢰할 수 있는 발급자가 발급한 생년월일 클레임의 날짜가 있는지 확인합니다. 클레임이 누락된 경우 권한 부여가 발생할 수 없으며, 이 경우 완료된 작업이 반환됩니다. 클레임이 있으면 사용자의 연령이 계산됩니다. 사용자가 요구 사항에 정의된 최소 연령을 충족하는 경우 권한 부여는 성공한 것으로 간주됩니다. 권한 부여가 성공하면 context.Succeed는 충족된 요구 사항을 유일한 매개 변수로 호출합니다.

여러 요구 사항에 처리기 사용

다음 예제에서는 권한 처리기가 세 가지 유형의 요구 사항을 처리할 수 있는 일 대 다 관계를 보여 줍니다.

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    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;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

앞의 코드는 성공으로 표시되지 않은 요구 사항을 포함하는 속성인 트래버스 PendingRequirements합니다. ReadPermission 요구 사항의 경우 요청된 리소스에 액세스하려면 사용자가 소유자 또는 스폰서여야 합니다. 요청된 DeletePermission 리소스에 EditPermission 액세스하려면 사용자가 소유자여야 합니다.

처리기 등록

구성 중에 서비스 컬렉션에 처리기를 등록합니다. 예를 들어:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

위의 코드에서는 services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();를 호출하여 MinimumAgeHandler를 싱글톤으로 등록합니다. 기본 제공 서비스 수명을 사용하여 처리기를 등록할 수 있습니다.

요구 사항과 처리기를 IAuthorizationRequirementIAuthorizationHandler 모두를 구현하는 단일 클래스로 번들로 묶을 수 있습니다. 이 번들링은 처리기와 요구 사항 간의 긴밀한 결합을 생성하며 간단한 요구 사항 및 처리기에만 권장됩니다. 두 인터페이스를 모두 구현하는 클래스를 만들면 요구 사항을 직접 처리할 수 있는 기본 제공 PassThroughAuthorizationHandler 로 인해 DI에 처리기를 등록할 필요가 없습니다.

완전한 자체 포함 클래스의 요구 사항 및 처리기 모두에 대한 좋은 예제 AssertionRequirementAssertionRequirement 클래스를 참조하세요.

처리기는 무엇을 반환해야 합니까?

처리기 예제Handle 메서드는 값을 반환하지 않습니다. 성공 또는 실패 상태는 어떻게 표시하나요?

  • 처리기는 context.Succeed(IAuthorizationRequirement requirement)를 호출하여 성공적으로 유효성이 검사된 요구 사항을 전달함으로써 성공을 나타냅니다.

  • 동일한 요구 사항에 대한 다른 처리기가 성공할 수 있기 때문에 처리기는 일반적으로 오류를 처리할 필요가 없습니다.

  • 다른 요구 사항 처리기가 성공하더라도 실패를 보장하려면 context.Fail을 호출합니다.

처리기에서 context.Succeed 또는 context.Fail을 호출하는 경우 다른 모든 처리기가 계속 호출됩니다. 이렇게 하면 다른 처리기가 요구 사항의 유효성을 성공적으로 검사하거나 실패한 경우에도 로깅과 같은 부작용이 발생할 수 있습니다. 설정 false하면 속성이 InvokeHandlersAfterFailure 호출되면 처리기 실행을 단락합니다 context.Fail . InvokeHandlersAfterFailure는 기본적으로 true로 설정되며, 이 경우 모든 처리기가 호출됩니다.

참고

인증이 실패하더라도 권한 부여 처리기가 호출됩니다.

요구 사항에 대해 여러 처리기를 원하는 이유는 무엇인가요?

OR 기준으로 평가하려는 경우 단일 요구 사항에 대해 여러 처리기를 구현합니다. 예를 들어 Microsoft에는 키 카드로만 열리는 문이 있습니다. 키 카드를 집에 그대로 두면 수신자가 임시 스티커를 인쇄하고 문을 엽니다. 이 시나리오에서는 BuildingEntry라는 단일 요구 사항이 있지만 각 요구 사항을 검사하는 여러 처리기가 있습니다.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

BadgeEntryHandler.cs

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            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;
    }
}

TemporaryStickerHandler.cs

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            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;
    }
}

두 처리기가 모두 등록되었는지 확인합니다. 정책이 BuildingEntryRequirement를 평가할 때 두 처리기가 성공하면 정책 평가가 성공합니다.

함수를 사용하여 정책 수행

정책을 이행하는 것이 코드로 표현하기 쉬운 상황이 있을 수 있습니다. RequireAssertion 정책 작성기를 사용하여 정책을 구성할 때 Func<AuthorizationHandlerContext, bool>을 제공할 수 있습니다.

예를 들어 이전 BadgeEntryHandler를 다음과 같이 다시 작성할 수 있습니다.

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

처리기에서 MVC 요청 컨텍스트에 액세스

권한 부여 처리기에서 구현하는 HandleRequirementAsync 메서드에는 두 개의 매개 변수, 즉 처리 중인 AuthorizationHandlerContextTRequirement가 있습니다. MVC 또는 SignalR과 같은 프레임워크는 추가 정보를 전달하기 위해 개체를 AuthorizationHandlerContextResource 속성에 자유롭게 추가할 수 있습니다.

엔드포인트 라우팅을 사용하는 경우 권한 부여는 일반적으로 권한 부여 미들웨어에 의해 처리됩니다. 이 경우 Resource 속성은 HttpContext의 인스턴스입니다. 컨텍스트를 사용하여 현재 엔드포인트에 액세스할 수 있습니다. 이 엔드포인트는 라우팅하는 기본 리소스를 프로브하는 데 사용할 수 있습니다. 예를 들어:

if (context.Resource is HttpContext httpContext)
{
    var endpoint = httpContext.GetEndpoint();
    var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
    ...
}

기존 라우팅을 사용하거나 MVC 권한 부여 필터의 일부로 권한 부여가 발생하는 경우 Resource의 값은 AuthorizationFilterContext 인스턴스입니다. 이 속성은 MVC 및 Razor Pages에서 제공되는 HttpContext, RouteData 및 모든 항목에 대한 액세스를 제공합니다.

속성의 Resource 사용은 프레임워크에 따라 다릅니다. Resource 속성의 정보를 사용하면 권한 부여 정책이 특정 프레임워크로 제한됩니다. 키워드를 Resourceis 사용하여 속성을 캐스팅한 다음 캐스트가 성공했는지 확인하여 코드가 다른 프레임워크에서 실행될 때 충돌 InvalidCastException 하지 않도록 합니다.

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

전역적으로 모든 사용자를 인증하도록 요구

모든 사용자의 인증을 전역으로 요구하는 방법에 대한 자세한 내용은 인증된 사용자 요구를 참조하세요.

역할 기반 권한 부여클레임 기반 권한 부여는 요구 사항, 요구 사항 처리기 및 미리 구성된 정책을 사용합니다. 이러한 구성 요소에서는 코드에서 권한 부여 평가의 식을 지원합니다. 그 결과 더 풍부하고 재사용이 가능하며 테스트가 가능한 권한 부여 구조가 생성됩니다.

권한 부여 정책은 하나 이상의 요구 사항으로 구성됩니다. 정책은 Startup.ConfigureServices 메서드에 권한 부여 서비스 구성의 일부로 등록됩니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}

앞의 예제에서 “AtLeast21” 정책이 생성되었습니다. 요구 사항에 대한 매개 변수로 제공되는 최소 기간의 단일 요구 사항이 있습니다.

IAuthorizationService

권한 부여가 성공했는지 확인하는 기본 서비스는 IAuthorizationService입니다.

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

위의 코드는 IAuthorizationService의 두 메서드를 강조 표시합니다.

IAuthorizationRequirement는 메서드가 없는 표식 서비스이며 권한 부여가 성공했는지 여부를 추적하기 위한 메커니즘입니다.

IAuthorizationHandler는 요구 사항이 충족되는지 확인하는 역할을 합니다.

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

AuthorizationHandlerContext 클래스는 처리기가 요구 사항이 충족되었는지 여부를 표시하는 데 사용하는 것입니다.

 context.Succeed(requirement)

다음 코드는 권한 부여 서비스의 간소화된(그리고 주석으로 주석이 추가된) 기본 구현을 보여 줍니다.

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

다음 코드는 일반적인 ConfigureServices를 보여 줍니다.

public void ConfigureServices(IServiceCollection services)
{
    // Add all of your handlers to DI.
    services.AddSingleton<IAuthorizationHandler, MyHandler1>();
    // MyHandler2, ...

    services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

    // Configure your policies
    services.AddAuthorization(options =>
          options.AddPolicy("Something",
          policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));


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

권한 부여를 위해 IAuthorizationService 또는 [Authorize(Policy = "Something")]을 사용합니다.

MVC 컨트롤러에 정책 적용

Razor Pages를 사용하는 경우 이 문서의 Razor Pages에 정책 적용을 참조하세요.

정책은 정책 이름으로 [Authorize] 특성을 사용하여 컨트롤러에 적용됩니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

Razor Pages에 정책 적용

정책 이름과 함께 [Authorize] 특성을 사용하여 Razor Pages에 정책을 적용합니다. 예를 들어:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

정책은 페이지 처리기 수준에서 적용 Razor 할 수 없으며 페이지에 적용해야 합니다.

정책은 권한 부여 규칙을 사용하여 Razor Pages에 적용할 수도 있습니다.

요구 사항

권한 부여 요구 사항은 정책이 현재 사용자 주체를 평가하는 데 사용할 수 있는 데이터 매개 변수의 컬렉션입니다. "AtLeast21" 정책에서 요구 사항은 단일 매개 변수(최소 기간)입니다. 요구 사항은 빈 표식 인터페이스인 구현합니다 IAuthorizationRequirement. 매개 변수가 있는 최소 연령 요구 사항은 다음과 같이 구현할 수 있습니다.

using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

권한 부여 정책에 여러 권한 부여 요구 사항이 포함된 경우 정책 평가가 성공하려면 모든 요구 사항이 통과되어야 합니다. 즉, 단일 권한 부여 정책에 추가된 여러 권한 부여 요구 사항은 AND 기준으로 처리됩니다.

참고

요구 사항에는 데이터 또는 속성이 필요하지 않습니다.

권한 부여 처리기

권한 부여 처리기는 요구 사항의 속성에 대한 평가를 담당합니다. 권한 부여 처리기는 제공된 AuthorizationHandlerContext 요구 사항을 평가하여 액세스가 허용되는지 확인합니다.

요구 사항에는 여러 처리기가 있을 수 있습니다. 처리기는 처리해야 하는 요구 사항이 있는 경우 TRequirement 상속AuthorizationHandler<TRequirement>할 수 있습니다. 또는 처리기가 둘 이상의 요구 사항 유형을 처리하도록 구현 IAuthorizationHandler 할 수 있습니다.

하나의 요구 사항에 처리기 사용

다음 예제에서는 최소 연령 처리기가 단일 요구 사항을 활용하는 일대일 관계를 보여 줍니다.

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            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;
    }
}

위의 코드는 현재 사용자 보안 주체에 알려진 신뢰할 수 있는 발급자가 발급한 생년월일 클레임의 날짜가 있는지 확인합니다. 클레임이 누락된 경우 권한 부여가 발생할 수 없으며, 이 경우 완료된 작업이 반환됩니다. 클레임이 있으면 사용자의 연령이 계산됩니다. 사용자가 요구 사항에 정의된 최소 연령을 충족하는 경우 권한 부여는 성공한 것으로 간주됩니다. 권한 부여가 성공하면 context.Succeed는 충족된 요구 사항을 유일한 매개 변수로 호출합니다.

여러 요구 사항에 처리기 사용

다음 예제에서는 권한 처리기가 세 가지 유형의 요구 사항을 처리할 수 있는 일 대 다 관계를 보여 줍니다.

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    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;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

앞의 코드는 성공으로 표시되지 않은 요구 사항을 포함하는 속성인 트래버스 PendingRequirements합니다. ReadPermission 요구 사항의 경우 요청된 리소스에 액세스하려면 사용자가 소유자 또는 스폰서여야 합니다. 요청된 DeletePermission 리소스에 EditPermission 액세스하려면 사용자가 소유자여야 합니다.

처리기 등록

구성 중에 서비스 컬렉션에 처리기를 등록합니다. 예를 들어:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

위의 코드에서는 services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();를 호출하여 MinimumAgeHandler를 싱글톤으로 등록합니다. 기본 제공 서비스 수명을 사용하여 처리기를 등록할 수 있습니다.

처리기는 무엇을 반환해야 합니까?

처리기 예제Handle 메서드는 값을 반환하지 않습니다. 성공 또는 실패 상태는 어떻게 표시하나요?

  • 처리기는 context.Succeed(IAuthorizationRequirement requirement)를 호출하여 성공적으로 유효성이 검사된 요구 사항을 전달함으로써 성공을 나타냅니다.

  • 동일한 요구 사항에 대한 다른 처리기가 성공할 수 있기 때문에 처리기는 일반적으로 오류를 처리할 필요가 없습니다.

  • 다른 요구 사항 처리기가 성공하더라도 실패를 보장하려면 context.Fail을 호출합니다.

처리기에서 context.Succeed 또는 context.Fail을 호출하는 경우 다른 모든 처리기가 계속 호출됩니다. 이렇게 하면 다른 처리기가 요구 사항의 유효성을 성공적으로 검사하거나 실패한 경우에도 로깅과 같은 부작용이 발생할 수 있습니다. 설정 false하면 속성이 InvokeHandlersAfterFailure 호출되면 처리기 실행을 단락합니다 context.Fail . InvokeHandlersAfterFailure는 기본적으로 true로 설정되며, 이 경우 모든 처리기가 호출됩니다.

참고

인증이 실패하더라도 권한 부여 처리기가 호출됩니다.

요구 사항에 대해 여러 처리기를 원하는 이유는 무엇인가요?

OR 기준으로 평가하려는 경우 단일 요구 사항에 대해 여러 처리기를 구현합니다. 예를 들어 Microsoft에는 키 카드로만 열리는 문이 있습니다. 키 카드를 집에 그대로 두면 수신자가 임시 스티커를 인쇄하고 문을 엽니다. 이 시나리오에서는 BuildingEntry라는 단일 요구 사항이 있지만 각 요구 사항을 검사하는 여러 처리기가 있습니다.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

BadgeEntryHandler.cs

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            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;
    }
}

TemporaryStickerHandler.cs

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            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;
    }
}

두 처리기가 모두 등록되었는지 확인합니다. 정책이 BuildingEntryRequirement를 평가할 때 두 처리기가 성공하면 정책 평가가 성공합니다.

함수를 사용하여 정책 수행

정책을 이행하는 것이 코드로 표현하기 쉬운 상황이 있을 수 있습니다. RequireAssertion 정책 작성기를 사용하여 정책을 구성할 때 Func<AuthorizationHandlerContext, bool>을 제공할 수 있습니다.

예를 들어 이전 BadgeEntryHandler를 다음과 같이 다시 작성할 수 있습니다.

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

처리기에서 MVC 요청 컨텍스트에 액세스

권한 부여 처리기에서 구현하는 HandleRequirementAsync 메서드에는 두 개의 매개 변수, 즉 처리 중인 AuthorizationHandlerContextTRequirement가 있습니다. MVC 또는 SignalR과 같은 프레임워크는 추가 정보를 전달하기 위해 개체를 AuthorizationHandlerContextResource 속성에 자유롭게 추가할 수 있습니다.

엔드포인트 라우팅을 사용하는 경우 권한 부여는 일반적으로 권한 부여 미들웨어에 의해 처리됩니다. 이 경우 Resource 속성은 Endpoint의 인스턴스입니다. 엔드포인트를 사용하여 라우팅하는 기본 리소스를 프로브할 수 있습니다. 예를 들어:

if (context.Resource is Endpoint endpoint)
{
   var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
   ...
}

엔드포인트는 현재 HttpContext에 대한 액세스를 제공하지 않습니다. 엔드포인트 라우팅을 사용하는 경우 권한 부여 처리기 내에서 액세스 HttpContext 하는 데 사용합니다IHttpContextAccessor. 자세한 내용은 사용자 지정 구성 요소에서 HttpContext 사용을 참조하세요.

기존 라우팅을 사용하거나 MVC 권한 부여 필터의 일부로 권한 부여가 발생하는 경우 Resource의 값은 AuthorizationFilterContext 인스턴스입니다. 이 속성은 MVC 및 Razor Pages에서 제공되는 HttpContext, RouteData 및 모든 항목에 대한 액세스를 제공합니다.

속성의 Resource 사용은 프레임워크에 따라 다릅니다. Resource 속성의 정보를 사용하면 권한 부여 정책이 특정 프레임워크로 제한됩니다. 키워드를 Resourceis 사용하여 속성을 캐스팅한 다음 캐스트가 성공했는지 확인하여 코드가 다른 프레임워크에서 실행될 때 충돌 InvalidCastException 하지 않도록 합니다.

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

전역적으로 모든 사용자를 인증하도록 요구

모든 사용자의 인증을 전역으로 요구하는 방법에 대한 자세한 내용은 인증된 사용자 요구를 참조하세요.