ASP.NET Core에서 사용자 정의 AuthorizeAttribute를 어떻게 작성합니까?
ASP.NET Core에서 사용자 지정 권한 부여 특성을 만들려고합니다. 이전 버전에서는 재정의 할 수있었습니다 bool AuthorizeCore(HttpContextBase httpContext). 그러나에 더 이상 존재하지 않습니다 AuthorizeAttribute.
사용자 정의 AuthorizeAttribute를 작성하는 현재 방법은 무엇입니까?
내가 달성하려는 것 : 헤더 인증에서 세션 ID를 받고 있습니다. 그 ID에서 특정 작업이 유효한지 알 수 있습니다.
ASP.Net Core 팀이 권장하는 접근 방식은 여기에 완전히 문서화 된 새로운 정책 설계를 사용하는 것 입니다. 새로운 접근 방식의 기본 아이디어는 새로운 [Authorize] 속성을 사용하여 "정책"을 지정하는 것입니다 (예 : [Authorize( Policy = "YouNeedToBe18ToDoThis")]정책이 응용 프로그램의 Startup.cs에 등록되어 일부 코드 블록을 실행하는 경우 (예 : 사용자에게 연령 주장이 있는지 확인) 나이가 18 세 이상인 경우).
정책 설계는 프레임 워크에 큰 도움이되며 ASP.Net Security Core 팀을 소개하도록 권장해야합니다. 즉, 모든 경우에 적합하지 않습니다. 이 접근 방식의 단점은 주어진 컨트롤러 또는 작업에 주어진 클레임 유형이 필요하다고 주장하는 가장 일반적인 요구에 편리한 솔루션을 제공 할 수 없다는 것입니다. 응용 프로그램에 개별 REST 리소스 ( "CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder"등)에 대한 CRUD 작업을 관리하는 수백 개의 개별 권한이있을 수있는 경우 새로운 접근 방식에는 일대일 반복 작업이 필요합니다. 정책 이름과 클레임 이름 간 매핑 (예 :options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));) 또는 런타임에 이러한 등록을 수행하기위한 코드 작성 (예 : 데이터베이스에서 모든 클레임 유형을 읽고 위에서 언급 한 호출을 루프로 수행). 대부분의 경우이 방법의 문제점은 불필요한 오버 헤드라는 것입니다.
ASP.Net Core Security 팀은 자체 솔루션을 만들지 말 것을 권장하지만 경우에 따라 가장 신중한 옵션이 될 수 있습니다.
다음은 IAuthorizationFilter를 사용하여 지정된 컨트롤러 또는 작업에 대한 클레임 요구 사항을 표현하는 간단한 방법을 제공하는 구현입니다.
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
저는 asp.net 보안 담당자입니다. 먼저이 중 어느 것도 뮤직 스토어 샘플 또는 단위 테스트 외부에 문서화되어 있지 않으며 노출 된 API 측면에서 여전히 개선되고 있음을 사과드립니다. 자세한 문서는 여기에 있습니다 .
사용자 정의 권한 부여 속성을 작성하지 않기를 바랍니다. 그렇게해야한다면 우리는 뭔가 잘못한 것입니다. 대신 인증 요구 사항을 작성해야합니다 .
인증은 신원에 따라 수행됩니다. ID는 인증에 의해 생성됩니다.
의견에서 헤더의 세션 ID를 확인하고 싶다고 말합니다. 세션 ID는 정체성의 기초가 될 것입니다. 이 Authorize속성 을 사용하려면 인증 미들웨어를 작성하여 해당 헤더를 가져 와서 인증 된로 변환하십시오 ClaimsPrincipal. 그런 다음 인증 요구 사항 내부에서이를 확인합니다. 승인 요구 사항은 원하는만큼 복잡 할 수 있습니다. 예를 들어, 현재 신원에 대한 생년월일 청구 날짜를 가지고 사용자가 18 세 이상인 경우이를 승인합니다.
public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
public override void Handle(AuthorizationHandlerContext context, Over18Requirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
{
context.Fail();
return;
}
var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
int age = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-age))
{
age--;
}
if (age >= 18)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
}
그런 다음 ConfigureServices()기능에 연결하십시오.
services.AddAuthorization(options =>
{
options.AddPolicy("Over18",
policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
});
마지막으로 컨트롤러 또는 액션 메소드에 적용하십시오.
[Authorize(Policy = "Over18")]
ASP.NET Core 2를 사용하면 다시 상속 할 수 AuthorizeAttribute있으며 구현 IAuthorizationFilter(또는 IAsyncAuthorizationFilter) 해야합니다 .
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _someFilterParameter;
public CustomAuthorizeAttribute(string someFilterParameter)
{
_someFilterParameter = someFilterParameter;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
// it isn't needed to set unauthorized result
// as the base class already requires the user to be authenticated
// this also makes redirect to a login page work properly
// context.Result = new UnauthorizedResult();
return;
}
// you can also use registered services
var someService = context.HttpContext.RequestServices.GetService<ISomeService>();
var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter);
if (!isAuthorized)
{
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
return;
}
}
}
컨트롤러 및 조치에서 사용자 정의 속성을 찾아 HandleRequirementAsync 메소드로 전달하는 고유 한 AuthorizationHandler를 작성할 수 있습니다.
public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
{
var attributes = new List<TAttribute>();
var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;
if (action != null)
{
attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType));
attributes.AddRange(GetAttributes(action.MethodInfo));
}
return HandleRequirementAsync(context, requirement, attributes);
}
protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes);
private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo)
{
return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
}
}
그런 다음 컨트롤러 또는 작업에 필요한 모든 사용자 지정 특성에 사용할 수 있습니다. 예를 들어 권한 요구 사항을 추가합니다. 사용자 정의 속성을 작성하십시오.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
public string Name { get; }
public PermissionAttribute(string name) : base("Permission")
{
Name = name;
}
}
그런 다음 정책에 추가 할 요구 사항을 작성하십시오.
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
//Add any custom requirement properties if you have them
}
그런 다음 이전에 생성 한 AttributeAuthorizationHandler를 상속하여 사용자 정의 속성에 대한 AuthorizationHandler를 작성하십시오. Controller 및 Action에서 누적 된 HandleRequirementsAsync 메서드의 모든 사용자 지정 특성에 대해 IEnumerable이 전달됩니다.
public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
{
foreach (var permissionAttribute in attributes)
{
if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
{
return;
}
}
context.Succeed(requirement);
}
private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
{
//Implement your custom user permission logic here
}
}
마지막으로 Startup.cs ConfigureServices 메소드에서 사용자 정의 AuthorizationHandler를 서비스에 추가하고 정책을 추가하십시오.
services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policyBuilder =>
{
policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
});
});
이제 커스텀 속성으로 컨트롤러와 액션을 간단하게 꾸밀 수 있습니다.
[Permission("AccessCustomers")]
public class CustomersController
{
[Permission("AddCustomer")]
IActionResult AddCustomer([FromBody] Customer customer)
{
//Add customer
}
}
Derek Greer GREAT 답변을 바탕으로 열거 형으로했습니다.
내 코드의 예는 다음과 같습니다.
public enum PermissionItem
{
User,
Product,
Contact,
Review,
Client
}
public enum PermissionAction
{
Read,
Create,
}
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(PermissionItem item, PermissionAction action)
: base(typeof(AuthorizeActionFilter))
{
Arguments = new object[] { item, action };
}
}
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly PermissionItem _item;
private readonly PermissionAction _action;
public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
{
_item = item;
_action = action;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)
if (!isAuthorized)
{
context.Result = new ForbidResult();
}
}
}
public class UserController : BaseController
{
private readonly DbContext _context;
public UserController( DbContext context) :
base()
{
_logger = logger;
}
[Authorize(PermissionItem.User, PermissionAction.Read)]
public async Task<IActionResult> Index()
{
return View(await _context.User.ToListAsync());
}
}
사용자 정의 AuthorizeAttribute를 작성하는 현재 방법은 무엇입니까
쉬움 : 나만의 것을 만들지 마십시오 AuthorizeAttribute.
순수한 권한 부여 시나리오 (특정 사용자에 대한 액세스 제한과 같은)의 경우 권장되는 접근 방식은 새 권한 부여 블록을 사용하는 것입니다. https://github.com/aspnet/MusicStore/blob/1c0aeb08bb1ebd846726232226279bbe001782e1/samples/MusicStore/Startup.cs#L84 -L92
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AuthorizationOptions>(options =>
{
options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore"));
});
}
}
public class StoreController : Controller
{
[Authorize(Policy = "ManageStore"), HttpGet]
public async Task<IActionResult> Manage() { ... }
}
인증을 위해서는 미들웨어 수준에서 처리하는 것이 가장 좋습니다.
정확히 달성하려고 무엇입니까?
현재 보안 관행을 사용하여 승인 단계에서 베어러 토큰의 유효성을 검사하려는 경우,
이것을 Startup / ConfigureServices에 추가하십시오
services.AddSingleton<IAuthorizationHandler, BearerAuthorizationHandler>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
services.AddAuthorization(options => options.AddPolicy("Bearer",
policy => policy.AddRequirements(new BearerRequirement())
)
);
그리고 이것은 코드베이스에서
public class BearerRequirement : IAuthorizationRequirement
{
public async Task<bool> IsTokenValid(SomeValidationContext context, string token)
{
// here you can check if the token received is valid
return true;
}
}
public class BearerAuthorizationHandler : AuthorizationHandler<BearerRequirement>
{
public BearerAuthorizationHandler(SomeValidationContext thatYouCanInject)
{
...
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, BearerRequirement requirement)
{
var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.Contains("Bearer"))
{
var token = authHeader.Replace("Bearer ", string.Empty);
if (await requirement.IsTokenValid(thatYouCanInject, token))
{
context.Succeed(requirement);
}
}
}
}
코드가 도달하지 않으면 context.Succeed(...)어쨌든 실패합니다 (401).
그런 다음 컨트롤러에서 사용할 수 있습니다
[Authorize(Policy = "Bearer", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
'development' 카테고리의 다른 글
| HTML의 새 탭에서 링크를 여는 방법은 무엇입니까? (0) | 2020.03.02 |
|---|---|
| Linux에서 cURL을 사용하는 HTTP POST 및 GET (0) | 2020.03.02 |
| SaaS, PaaS 및 IaaS 란 무엇입니까? (0) | 2020.03.02 |
| Java에서 맵 값을 증가시키는 가장 효율적인 방법 (0) | 2020.03.02 |
| 인증 플러그인 'caching_sha2_password'를로드 할 수 없습니다 (0) | 2020.03.02 |