ASP.Net Identity встроенные функции с настраиваемыми таблицами в ASP.Net Core - PullRequest
0 голосов
/ 12 сентября 2018

Я использую ASP.Net Core Web Api 2 на .Net 2.1 Framework. У меня есть пользовательские таблицы AppUsers и AppRoles, связанные с таблицей мостов AppUserRoles

Моя основная проблема заключается в том, что я хочу использовать [Authorize(Roles = "UserRole")] КакUser.Identity работает нормально, и я получаю идентификатор пользователя от User.Identity.Name Я думал, что есть какой-то способ установить роли и проверить их перед запросом контроллера, или использовать User.IsInRole("UserRole") для проверки внутри контроллера.

Isможно как-то перестроить или перегрузить .IsInRole("UserRole") функцию или [Authorize(Roles = "UserRole")] атрибутную фоновую функцию, чтобы я мог написать собственную логику для проверки прав доступа пользователя?Или установить мои таблицы как таблицы по умолчанию, чтобы они могли работать на собственной логике.Для моей задачи скорость важна так же, как и безопасность.

Я открыт для предложений, если есть какой-то другой способ, но моя цель также состоит в том, чтобы лучше понять эти функции.

1 Ответ

0 голосов
/ 12 сентября 2018

Вам не нужно переопределять Authorize или IsInRole. Просто добавьте роли в качестве претензии к User.Identity. Вы можете использовать промежуточное ПО для преобразования заявок.

В качестве примера я предлагаю вам взглянуть на PolicyServer . У него такой же подход. бесплатная версия OSS добавляет утверждения в промежуточное ПО.

/// Add the policy server claims transformation middleware to the pipeline.
/// This middleware will turn application roles and permissions into claims
/// and add them to the current user
public static IApplicationBuilder UsePolicyServerClaims(this IApplicationBuilder app)
{
    return app.UseMiddleware<PolicyServerClaimsMiddleware>();
}

Где PolicyServerClaimsMiddleware:

public class PolicyServerClaimsMiddleware
{
    private readonly RequestDelegate _next;

    /// <summary>
    /// Initializes a new instance of the <see cref="PolicyServerClaimsMiddleware"/> class.
    /// </summary>
    /// <param name="next">The next.</param>
    public PolicyServerClaimsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    /// <summary>
    /// Invoke
    /// </summary>
    /// <param name="context">The context.</param>
    /// <param name="client">The client.</param>
    /// <returns></returns>
    public async Task Invoke(HttpContext context, IPolicyServerRuntimeClient client)
    {
        if (context.User.Identity.IsAuthenticated)
        {
            var policy = await client.EvaluateAsync(context.User);

            var roleClaims = policy.Roles.Select(x => new Claim("role", x));
            var permissionClaims = policy.Permissions.Select(x => new Claim("permission", x));

            var id = new ClaimsIdentity("PolicyServerMiddleware", "name", "role");
            id.AddClaims(roleClaims);
            id.AddClaims(permissionClaims);

            context.User.AddIdentity(id);
        }
        await _next(context);
    }
}

А из автозагрузки:

public void ConfigureServices(IServiceCollection services)
{

    services.AddMvcCore(options =>
    {
        // workaround: https://github.com/aspnet/Mvc/issues/7809
        options.AllowCombiningAuthorizeFilters = false;
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddAuthorization();

    // This is not relevant for you, but just to show how policyserver is implemented.
    // The bottom line is that you can implement this anyway you like.

    // this sets up the PolicyServer client library and policy
    // provider - configuration is loaded from appsettings.json
    services.AddPolicyServerClient(Configuration.GetSection("Policy"))
        .AddAuthorizationPermissionPolicies();

}

public void Configure(IApplicationBuilder app)
{
    app.UseAuthentication();

    // add this middleware to make roles and permissions available as claims
    // this is mainly useful for using the classic [Authorize(Roles="foo")] and IsInRole functionality
    // this is not needed if you use the client library directly or the new policy-based authorization framework in ASP.NET Core
    app.UsePolicyServerClaims();

    app.UseMvc();
}

В этом примере конфигурация считывается из файла, который также может быть вам полезен. Но вы также можете реализовать магазин и добавить кеширование.

Если вы хотите добавить логику авторизации, я предлагаю вам создать некоторые политики и обработчики авторизации. Просто убедитесь, что вы используете промежуточное программное обеспечение в нужном месте.


Альтернативой является использование собственного фильтра / атрибута:

//using Microsoft.AspNetCore.Authorization;
//using Microsoft.AspNetCore.Mvc;
//using Microsoft.AspNetCore.Mvc.Filters;

public class CustomPolicyAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
    private int _number;

    public CustomPolicyAttribute(int number)
    {
        _number = number;
    }

    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        var service = (IAuthorizationService)context.HttpContext.RequestServices.GetService(typeof(IAuthorizationService));

        var requirement = new CustomRequirement
        {
            Number = _number
        };
        var result = await service.AuthorizeAsync(context.HttpContext.User, null, requirement);
        if (!result.Succeeded)
            context.Result = new ForbidResult();
    }
}

Вы можете использовать это несколькими способами. Использовать как атрибут (эквивалент Авторизации):

[CustomPolicy(1)]
public async Task<IActionResult> DoSomething()
{

}

Или подтвердить вручную (эквивалент IsInRole):

public class MyController : Controller
{
    private readonly IAuthorizationService _authorizationService;

    public MyController(IAuthorizationService authorizationService)
    {
        _authorizationService = authorizationService;
    }

    public async Task<IActionResult> DoSomething(int number)
    {
        var requirement = new CustomRequirement
        {
            Number = number
        };
        var result = await _authorizationService.AuthorizeAsync(User, null, requirement);
        if (!result.Succeeded) return Forbid();

        return View("success");
    }
}

Вам понадобится AuthorizationHandler для оценки требования:

public class CustomRequirementHandler : AuthorizationHandler<CustomRequirement>
{
    // Use dependency injection to include services you need.
    public CustomRequirementHandler ()
    {
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
    {
        // Add your advanced check here.
        if (requirement.Number > 0)
        {
            context.Succeed(requirement);
        }
    }
}

И зарегистрируйте это в автозагрузке:

services.AddTransient<IAuthorizationHandler, CustomRequirementHandler>();

В обработчике вы можете добавить свою собственную логику. В этом случае вам не нужно будет добавлять политики и вам не нужно добавлять авторизацию в качестве утверждений.

...