ASP.NET Core 권한 부여 정책은 프레임워크가 사용자가 리소스에 액세스할 수 있는지 여부를 결정하기 위해 평가하는 하나 이상의 권한 부여 요구 사항의 명명된 집합입니다.
이 문서는 다음 사항을 설명합니다.
- 요구 사항을 만드는 방법.
- 정책을 등록하고 적용하는 방법입니다.
- 단일 및 다중 요구 사항 평가를 위한 권한 부여 처리기입니다.
- 단일 정책의 여러 요구 사항을 평가하는 방법
실제로 정책은 [Authorize(Policy = "...")](Razor 구성 요소, 페이지 및 컨트롤러) 또는 RequireAuthorization(...)(엔드포인트)에 적용되고, 프레임워크는 처리기를 사용하여 정책의 기반이 되는 요구 사항을 평가합니다.
IAuthorizationPolicyProvider(ASP.NET Core 설명서의 사용자 지정 권한 부여 정책 공급자)는 앱 시작 시 정책을 등록하는 대신 동적으로 정책을 생성합니다.
역할 기반 권한 부여 및 클레임 기반 권한 부여 는 요구 사항, 요구 사항 처리기 및 미리 구성된 권한 부여 정책을 사용합니다. 이러한 구성 요소에서는 코드에서 권한 부여 평가의 식을 지원합니다.
이 문서에서는 Razor 구성 요소 예제를 사용하고 ASP.NET Core 3.1 이상에 대한 Blazor 권한 부여 시나리오에 중점을 둡니다. Razor 모든 ASP.NET Core 릴리스에 적용되는 Pages 및 MVC 지침은 이 문서를 읽은 후 다음 리소스를 참조하세요.
이 문서의 일부 예제(ASP.NET Core 8.0 이상)는 C# 12(.NET 8) 이상에서 사용할 수 있는 기본 생성자를 사용합니다. 자세한 내용은 클래스 및 구조체에 대한 기본 생성자 선언(C# 설명서 자습서) 및 기본 생성자(C# 가이드)를 참조하세요.
요구 사항 및 정책 등록
권한 부여 정책은 현재 사용자 보안 주체에 대한 권한 부여를 평가하기 위해 정책에서 사용하는 하나 이상의 요구 사항으로 구성됩니다. 요구 사항은 IAuthorizationRequirement을(를) 구현하며, 이는 빈 표식 인터페이스입니다.
요구 사항에 데이터가 없거나 속성(매개 변수)이 없는 경우 권한 부여를 처리하기 위해 연결된 권한 부여 처리기 (IAuthorizationHandler)를 트리거하는 빈 표식 역할을 합니다(이 문서의 뒷부분에서 자세히 설명). 이 경우 처리기는 전적으로 HTTP 컨텍스트, 사용자 클레임 또는 백 엔드 데이터를 사용하여 요구 사항을 충족하는 사용자에 대한 결정을 내리기 때문에 요구 사항 클래스 자체에는 내부 데이터 또는 매개 변수가 필요하지 않습니다. 요구 사항은 평가할 규칙만 프레임워크에 지시합니다.
예를 들어 표식 클래스로만 구현되는 다음과 같은 최소 연령 요구 사항(MinimumAgeRequirement)을 고려합니다.
public class MinimumAgeRequirement : IAuthorizationRequirement { }
이전 요구 사항은 사용자가 처리기가 확인하는 특정 연령을 초과했음을 확인하는 정책을 만드는 데 사용됩니다.
AuthorizationHandler<MinimumAgeRequirement>는 AuthorizationHandlerContext.User를 검사합니다. 사용자에게 특정 연령을 초과했음을 나타내는 생년월일 클레임이 있는 경우 요구 사항이 성공합니다. 이 경우 요구 사항 개체에는 속성(매개 변수)이 필요하지 않습니다. 다음 예제에서는 최소 연령을 설정하는 매개 변수가 있는 최소 연령 요구 사항의 전체 구현을 보여 줍니다.
사용자 권한 부여를 평가하기 위해 단일 매개 변수인 최소 기간을 설명하는 다음 MinimumAgeRequirement 요구 사항을 고려합니다.
using Microsoft.AspNetCore.Authorization;
namespace BlazorWebAppAuthorization.Policies.Requirements;
public class MinimumAgeRequirement(int minimumAge) : IAuthorizationRequirement
{
public int MinimumAge { get; } = minimumAge;
}
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public MinimumAgeRequirement(int minimumAge) =>
MinimumAge = minimumAge;
public int MinimumAge { get; }
}
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge)
{
MinimumAge = minimumAge;
}
}
정책은 앱의 Program 파일에서 AuthorizationBuilder.AddPolicy를 호출하여 권한 부여 서비스 구성의 일부로 등록됩니다. 다음 예제에서는 최소 연령의 단일 요구 사항을 사용하여 정책을 만들고 AtLeast21 최소 연령을 21세로 설정합니다.
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
정책은 앱의 Program 파일에서 AuthorizationBuilder.AddPolicy를 호출하여 권한 부여 서비스 구성의 일부로 등록됩니다. 다음 예제에서는 최소 연령의 단일 요구 사항을 사용하여 정책을 만들고 AtLeast21 최소 연령을 21세로 설정합니다.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
정책은 Startup.ConfigureServices를 호출하여 Startup.cs(AuthorizationBuilder.AddPolicy)에서 권한 부여 서비스 구성의 일부로 등록됩니다. 다음 예제에서는 최소 연령의 단일 요구 사항을 사용하여 정책을 만들고 AtLeast21 최소 연령을 21세로 설정합니다.
services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
권한 부여 정책에 여러 권한 부여 요구 사항이 포함된 경우 정책 평가가 성공하려면 모든 요구 사항을 통과해야 합니다. 즉, 단일 권한 부여 정책에 추가된 여러 권한 부여 요구 사항은 AND 기준으로 처리됩니다.
구성 요소에 Razor 정책 적용
정책 이름을 사용하여 Razor 특성으로 [Authorize] 구성 요소에 정책을 적용합니다.
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]
여러 정책이 적용되는 경우 액세스 권한이 부여되기 전에 모든 정책을 통과해야 합니다.
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]
@attribute [Authorize(Policy = "HumanResourcesMember")]
엔드포인트에 정책 적용
정책 이름과 함께 RequireAuthorization을 사용하여 엔드포인트에 정책을 적용합니다. 다음은 그 예입니다.
app.MapGet("/helloworld", () => "Hello World!")
.RequireAuthorization("AtLeast21");
MVC 및 Razor Pages 앱에서 정책 적용
Pages 및 MVC 앱에서 Razor 정책을 적용하는 방법에 대한 지침은 다음 리소스를 참조하세요.
권한 부여 서비스 인터페이스(IAuthorizationService)
IAuthorizationService 은 주로 오버로드를 호출할 때 IAuthorizationService.AuthorizeAsync 권한 부여가 성공하는지 확인하는 역할을 담당합니다.
-
AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements): 사용자가 지정된 리소스에 대한 특정 권한 부여 요구 사항 집합을 충족하는지 확인합니다. -
AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName): 사용자가 지정된 리소스에 대한 특정 권한 부여 정책을 충족하는지 확인합니다.
정책 평가에 리소스가 필요하지 않은 경우 리소스에는 null가 전달됩니다.
앞선 메서드는 AuthorizationResult로 래핑된 Task를 반환합니다.
각 IAuthorizationHandler는 IAuthorizationHandler.HandleAsync를 통해 요구 사항이 충족되었는지 확인할 책임이 있습니다. 클래스에는 AuthorizationHandlerContext 구현에서 사용하는 권한 부여 정보가 포함됩니다 IAuthorizationHandler . IAuthorizationRequirement 는 권한 부여가 성공했는지 여부를 추적하는 메커니즘으로 사용되는 메서드가 없는 표식 인터페이스입니다. AuthorizationHandlerContext.Succeed이 IAuthorizationRequirement와 함께 호출되면 정책이 충족됩니다:
context.Succeed(requirement);
권한 부여 처리기
권한 부여 처리기는 요구 사항의 속성에 대한 평가를 담당합니다. 권한 부여 처리기는 제공된 AuthorizationHandlerContext 대해 요구 사항을 평가하여 access 허용되는지 확인합니다.
요구 사항에는 여러 처리기가 있을 수 있습니다. 처리기는 AuthorizationHandler<TRequirement>를 상속할 수 있으며, 여기서 TRequirement는 처리해야 하는 요구 사항입니다. 또는 처리기가 둘 이상의 요구 사항을 처리하기 위해 직접 구현 IAuthorizationHandler 할 수 있습니다.
하나의 요구 사항에 처리기 사용
다음 예제에서는 최소 연령 처리기가 단일 요구 사항을 처리하는 일 대 일 관계를 보여 줍니다.
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;
namespace BlazorWebAppAuthorization.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);
if (dateOfBirthClaim is null)
{
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
{
var dateOfBirthClaim =
context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);
if (dateOfBirthClaim is null)
{
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
{
// 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).Value);
var calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
// 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.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;
namespace BlazorWebAppAuthorization.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;
}
}
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
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);
}
}
}
// 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 요구 사항의 경우, 사용자는 요청된 리소스에 접근하려면 소유자이거나 스폰서여야 합니다.
EditPermission 또는 DeletePermission 요구 사항의 경우, 요청된 리소스에 접근하려면 소유자여야 합니다.
처리기 등록
구성 중에 서비스 컬렉션에 처리기를 등록합니다. 다음 예제에서는 최소 연령 처리기(MinimumAgeHandler)를 싱글톤 서비스로 등록하지만, 처리기는 기본 제공 서비스 수명 중 어느 것을 사용해서도 등록할 수 있습니다:
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
요구 사항과 처리기를 IAuthorizationRequirement 및 IAuthorizationHandler 모두를 구현하는 단일 클래스로 번들로 묶을 수 있습니다. 이렇게 하면 처리기와 요구 사항이 긴밀하게 결합됩니다. 이는 간단한 요구 사항 및 처리기에만 권장됩니다. 두 인터페이스를 모두 구현하는 클래스를 만들면 요구 사항을 직접 처리할 수 있는 기본 제공으로 인해 서비스 컨테이너에 PassThroughAuthorizationHandler 처리기를 등록할 필요가 없습니다.
AssertionRequirement가 완전히 자체 포함된 클래스에서 요구 사항이자 처리기 역할을 모두 수행하는 예제는 ASP.NET Core AssertionRequirement을 참조하세요.
AssertionRequirement 프레임워크의 API를 사용하면 별도의 상용구 요구 사항 및 처리기 클래스를 작성하는 대신 인라인 람다 식을 사용하여 액세스의 유효성을 검사할 수 있습니다.
Note
문서 링크는 .NET 참조 소스를 가리키며, 일반적으로 저장소의 기본 브랜치를 로드합니다. 이는 .NET의 다음 릴리스를 위한 현재 개발 상태를 나타냅니다. 특정 릴리스를 위한 태그를 선택하려면 Switch branches or tags 드롭다운 목록을 사용하세요. 자세한 내용은 ASP.NET Core 소스 코드(dotnet/AspNetCore.Docs #26205)의 버전 태그를 선택하는 방법을 참조하세요.
처리기는 무엇을 반환해야 합니까?
Handle의 메서드는 값을 반환하지 않습니다. 성공 또는 실패 상태는 어떻게 표시하나요?
처리기는
context.Succeed를 호출하고, 성공적으로 유효성 검사를 통과한 요구 사항(IAuthorizationRequirement)을 전달하여 성공을 나타냅니다.동일한 요구 사항에 대한 다른 처리기가 성공할 수 있으므로 일반적으로 오류를 처리하는 데는 처리기가 필요하지 않습니다.
다른 요구 사항 처리기가 성공하더라도 실패를 보장하려면
context.Fail을 호출합니다.
처리기에서 context.Succeed 또는 context.Fail을 호출하는 경우 다른 모든 처리기가 계속 호출됩니다. 이렇게 하면 다른 처리기가 요구 사항의 유효성을 성공적으로 검사하거나 실패하더라도 발생하는 로깅과 같은 부작용을 생성할 수 있습니다.
false로 설정하면, InvokeHandlersAfterFailure가 호출될 때 context.Fail 속성이 처리기의 실행을 단락시킵니다.
InvokeHandlersAfterFailure는 기본적으로 true로 설정되며, 이 경우 모든 처리기가 호출됩니다.
Note
인증이 실패하더라도 권한 부여 처리기가 호출됩니다. 처리기는 어떤 순서로든 실행할 수 있으므로 호출 처리기의 순서에 따라 달라 지지 않습니다 .
요구 사항에 대해 여러 처리기를 원하는 이유는 무엇인가요?
OR 기준으로 평가하려는 경우 단일 요구 사항에 대해 여러 처리기를 구현합니다. 예를 들어 Contoso Corporation에 키 카드로만 열리는 문이 있다고 가정합니다. 열쇠 카드를 집에 두면 접수원은 임시 스티커를 인쇄하고 문을 엽니다. 이 시나리오에서 앱에는 요구 사항이 하나만 있지만 이를 검사하는 처리기는 여러 개입니다.
다음 예제 구현에서는 다음을 수행합니다.
-
BuildingEntryRequirement는 건물 진입 요구 사항입니다. -
BadgeEntryHandler(해당 개인이 배지를 가지고 있음)와TemporaryStickerHandler(해당 개인이 임시 스티커를 가지고 있음)는 별개의 처리기이며, 각각 하나의 요구 사항만 검사합니다.
BuildingEntryRequirement.cs:
using Microsoft.AspNetCore.Authorization;
namespace BlazorWebAppAuthorization.Policies.Requirements;
public class BuildingEntryRequirement : IAuthorizationRequirement { }
BadgeEntryHandler.cs:
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;
namespace BlazorWebAppAuthorization.Policies.Handlers;
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "BadgeId"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
TemporaryStickerHandler.cs:
using Microsoft.AspNetCore.Authorization;
using BlazorWebAppAuthorization.Policies.Requirements;
namespace BlazorWebAppAuthorization.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://contososecurity"))
{
// Code to check expiration date omitted for brevity.
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
BuildingEntryRequirement.cs:
using Microsoft.AspNetCore.Authorization;
public class BuildingEntryRequirement : IAuthorizationRequirement
{
}
BadgeEntryHandler.cs:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c =>
c.Type == "BadgeId" &&
c.Issuer == "https://contososecurity"))
{
context.Succeed(requirement);
}
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
BuildingEntryRequirement requirement)
{
if (context.User.HasClaim(c =>
c.Type == "TemporaryBadgeId" &&
c.Issuer == "https://contososecurity"))
{
// We'd also check the expiration date on the sticker.
context.Succeed(requirement);
}
// Use the following if targeting a version of
// .NET Framework older than 4.6:
// return Task.FromResult(0);
return Task.CompletedTask;
}
}
두 처리기가 모두 등록되었는지 확인합니다. 정책이 평가할 BuildingEntryRequirement때 처리기 중 하나가 성공하면 정책 평가가 성공합니다.
Func을 사용하여 정책을 준수합니다
정책 빌더 Func<AuthorizationHandlerContext, bool>로 정책을 구성할 때 RequireAssertion 대리자를 사용하면 정책 충족을 코드로 간단히 표현할 수 있는 경우가 있습니다. 예를 들어 앞 BadgeEntryHandler 의 내용은 다음과 같이 다시 작성할 수 있습니다.
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
(c.Type == "BadgeId" || c.Type == "TemporaryBadgeId")
&& c.Issuer == "https://contososecurity")));
});
// <snippet_minimumAgeHandlerRegistration>
services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == "BadgeId" ||
c.Type == "TemporaryBadgeId") &&
c.Issuer == "https://microsoftsecurity")));
});
전역 사용자 인증 필요
모든 앱 사용자에 대해 인증을 요구하는 방법에 대한 자세한 내용은 권한 부여로 보호되는 사용자 데이터로 ASP.NET Core 앱 만들기 참조하세요.
외부 서비스 샘플을 통한 권한 부여
외부 서비스 샘플(dotnet/AspNetCore.Docs.SamplesGitHub 리포지토리)을 통한 권한 부여는 외부 권한 부여 서비스를 사용하여 추가 권한 부여 요구 사항을 구현하는 방법을 보여 줍니다. 솔루션의 Contoso.API 프로젝트는 Microsoft Entra ID 사용하여 보호됩니다.
Contoso.Security.API project 추가 권한 부여 검사는 Contoso.API 클라이언트 앱이 GetWeather API를 호출할 수 있는지 여부를 설명하는 페이로드를 반환합니다.
샘플 구성
다음 데모에서는 명령 셸에서 NSwag(Swagger/OpenAPI) 또는 cURL 을 사용합니다.
Contoso.Security.API 프로젝트에서 AllowedClients 자리 표시자({CLIENT ID})를 임의의 테스트 GUID 값(예: 00001111-aaaa-2222-bbbb-3333cccc4444)으로 설정하세요:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AllowedClients": [
"{CLIENT ID (FOR THE CLIENT CALLING CONTOSO.API)}"
]
}
Contoso.API 프로젝트에서 연 명령 셸에서 dotnet user-jwts을 사용하여, 앞선 단계에서 만든 클라이언트 앱의 ID(예: appid)에 대한 00001111-aaaa-2222-bbbb-3333cccc4444 클레임이 포함된 액세스 토큰을 생성합니다.
dotnet user-jwts create --claim appid={GUID}
Example:
dotnet user-jwts create --claim appid=00001111-aaaa-2222-bbbb-3333cccc4444
출력은 명령 셸에서 "Token:" 후 토큰을 생성합니다.
New JWT saved with ID '{JWT ID}'.
Name: {USER}
Custom Claims: [appid=00001111-aaaa-2222-bbbb-3333cccc4444]
Token: {TOKEN}
나중에 사용할 수 있도록 토큰의 값(앞의 출력에 자리 표시자가 표시되는 위치 {TOKEN} )을 따로 설정합니다.
온라인 디코더에서 JWT 토큰을 디코딩할 수 있습니다(예: jwt.ms 해당 콘텐츠를 확인하여 클라이언트 앱의 ID가 있는 클레임이 포함됨 appid ).
{
"alg": "HS256",
"typ": "JWT"
}.{
"unique_name": "{USER}",
"sub": "{USER}",
"jti": "14ed7729",
"appid": "{CLIENT ID}",
"aud": [
"https://localhost:7250",
"http://localhost:7251"
],
"nbf": 1780660887,
"exp": 1788609687,
"iat": 1780660888,
"iss": "dotnet-user-jwts"
}.[Signature]
잘못된 클라이언트 ID(appid) 값으로 명령을 다시 실행합니다.
dotnet user-jwts create --claim appid=aaaabbbb-0000-cccc-1111-dddd2222eeee
두 번째 토큰의 값을 따로 설정합니다.
Visual Studio에서 또는 명령 셸에서 Contoso.API 명령을 사용하여 Contoso.Security.API 및 dotnet watch 프로젝트를 모두 시작하세요.
dotnet watch
프로젝트의 Swagger UI Contoso.API (https://localhost:7250/swagger/index.html)에서 권한 부여 단추를 선택합니다.
사용 가능한 권한 부여: Bearer 창에서 액세스 토큰을 입력합니다. 권한 부여 단추를 선택합니다. 사용 가능한 권한 부여 창을 닫습니다.
기본값 아래에서 엔드포인트의 /WeatherForecast 버튼을 선택합니다.
사용해 보기 단추를 선택합니다.
실행 단추를 선택합니다.
응답>서버 응답>본문의 출력은 프로젝트에서 반환된 일기 예보 JSON을 보여 줍니다Contoso.API.
잘못된 클라이언트 앱 ID로 생성된 액세스 토큰으로 동일한 단계를 수행합니다. 응답은 403 - 금지됨입니다.
추가 리소스
ASP.NET Core