it-swarm-ko.com

ASP.NET Core에서 사용자 정의 AuthorizeAttribute를 어떻게 만듭니 까?

ASP.NET 코어에서 사용자 지정 권한 부여 특성을 만들려고합니다. 이전 버전에서는 bool AuthorizeCore(HttpContextBase httpContext)을 무시할 수있었습니다. 그러나 이것은 더 이상 AuthorizeAttribute 에 존재하지 않습니다.

사용자 정의 AuthorizeAttribute를 만드는 현재 접근 방식은 무엇입니까?

내가 성취하려고하는 것 : Header Authorization에 세션 ID를 받고 있습니다. 해당 ID에서 특정 작업이 유효한 지 여부를 알 수 있습니다.

293
jltrem

ASP.Net 핵심 팀이 권장하는 접근 방식은 완전히 문서화 된 새로운 정책 설계를 사용하는 것입니다. { 여기 }. 새로운 접근법의 기본 개념은 새로운 [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 핵심 보안 팀은 자신의 솔루션을 결코 만들지 말 것을 권장하지만 어떤 경우에는 이것이 시작하는 데있어 가장 신중한 선택 일 수 있습니다.

다음은 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();
    }
}
295
Derek Greer

나는 asp.net 보안 사람이다. 먼저 음악 스토어 샘플 또는 유닛 테스트 외부에이 문서가 아직 문서화되어 있지 않으며, 공개 된 API의 관점에서 아직 완전히 정제되지 않았다는 점을 사과 드리겠습니다. 자세한 문서는 here 입니다.

사용자 정의 권한 부여 특성을 작성하는 것을 원하지 않습니다. 그렇게해야한다면 우리는 뭔가 잘못 했어. 대신 권한 requirements 를 작성해야합니다.

권한 부여는 신분증에 적용됩니다. ID는 인증에 의해 생성됩니다.

댓글에서 헤더의 세션 ID를 확인하려고합니다. 세션 ID는 신원의 기초가됩니다. Authorize 특성을 사용하려면 인증 미들웨어를 작성하여 해당 헤더를 가져와 인증 된 ClaimsPrincipal으로 변환하십시오. 그런 다음 승인 요구 사항을 확인하십시오. 권한 부여 요구 사항은 원하는대로 복잡 할 수 있습니다. 예를 들어 현재 ID에 대한 생년월일 청구가 있고 사용자가 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")]
220
blowdart

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;
        }
    }
}
58
gius

사용자 정의 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() { ... }
}

인증을 위해서는 미들웨어 수준에서 처리하는 것이 가장 좋습니다.

정확히 달성하려는 것은 무엇입니까?

23
Pinpoint

컨트롤러 및 액션에서 사용자 정의 속성을 찾아 자신의 AuthorizationHandler를 만들어 HandleRequirementAsync 메소드에 전달할 수 있습니다.

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를 만듭니다. 컨트롤러 및 액션에서 누적 된 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
    }
}
22
Shawn

데릭 그리어 (Derek Greer) GREAT 답변에 따라, 저는 enum으로 해냈습니다.

내 코드 예제는 다음과 같습니다.

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());
    }
}
21
bruno.almeida