Атрибут ASP.NET Core Custom Authorize - PullRequest
2 голосов
/ 30 мая 2019

У меня есть такая конфигурация авторизации. Проблема в том, что я хочу проверить субмодуль и есть ли у него права. У меня также есть контроллер входа в систему, где я создаю заявки и т. Д. И добавляю их. Но я думаю, что я что-то здесь тоже упускаю. Я должен добавить претензии к текущему участнику, я думаю, но я не знаю, как:

Вот мое действие входа в систему в контроллере входа

[HttpGet("login")]
    public IActionResult Login()
    {
        var userName = _httpContextAccessor.HttpContext.User.Identity.Name.GetUserNameFromHttpContext();
        var user = _userClient.GetUserByUserName(userName);
        var loggedIUserDto = new LoggedInUserDto {UserName = userName};
        var claims = new List<Claim>();

        if (user == null)
        {
            return Unauthorized($"User {userName} does not exits in DB"); 
        }

        var userModulesWithSubmodules = _loginClient.GetUserModulesWithSubmodules(userName);
        loggedIUserDto.UserModulesWithSubmodules = userModulesWithSubmodules;

        if (userModulesWithSubmodules.Count == 0)
        {
            return Conflict($"User {userName} has no modules"); 
        }

        foreach (var module in userModulesWithSubmodules)
        {
            foreach (var submodule in module.Submodules)
            {
                var submoduleActionList = new List<string>();
                if (submodule.CanAdd)
                {
                    submoduleActionList.Add("CanAdd");
                }

                if (submodule.CanEdit)
                {
                    submoduleActionList.Add("CanEdit");
                }

                if (submodule.CanRead)
                {
                    submoduleActionList.Add("CanRead");
                }

                if (submodule.CanDelete)
                {
                    submoduleActionList.Add("CanDelete");
                }

                claims.Add(new Claim(ClaimTypes.Name, user.UserName));
                claims.Add(new Claim(submodule.SubmoduleName, string.Join(',', submoduleActionList)));
            }

        }

        var claimsIdentity = new ClaimsIdentity(claims);
        var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

        Thread.CurrentPrincipal = claimsPrincipal;

        return Ok(loggedIUserDto);
    }

CustomeAuthorize

    public class CustomAuthorize : AuthorizeAttribute
   {
    private SubmoduleActionType _submoduleActionType;
    private SubmoduleType _submoduleType;

    public SubmoduleActionType ActionType;

    public SubmoduleType Type {
        get => _submoduleType;
        set
        {
            _submoduleType = value;
            Policy = $"{_submoduleType.ToString()};{_submoduleActionType.ToString()}";
        }
    }

    public CustomAuthorize(SubmoduleActionType submoduleActionType, SubmoduleType submoduleType)
    {
        _submoduleActionType = submoduleActionType;
        _submoduleType = submoduleType;
    }
}

Ниже приведен мой класс требований:

    public class SubmoduleTypeRequirement : IAuthorizationRequirement
{
    public SubmoduleActionType? ActionType { get; set; }

    public SubmoduleType? Type { get; set; }

    public SubmoduleTypeRequirement(SubmoduleActionType actionType, SubmoduleType type)
    {
        Type = type;
        ActionType = actionType;
    }
}

Вот мой класс Handler

  public class SubmoduleAuthorizationHandler : AuthorizationHandler<SubmoduleTypeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SubmoduleTypeRequirement submoduleRequirement)
    {
        if (!submoduleRequirement.ActionType.HasValue)
        {
            throw new ArgumentException("No action type provided");
        }

        if (!submoduleRequirement.Type.HasValue)
        {
            throw new ArgumentException("No submodule type provided");
        }

        if (!context.User.HasClaim(uc => uc.Type == submoduleRequirement.Type.ToString()))
        {
            context.Fail();
            return Task.FromResult(0);
        }

        var grantedRights = Convert.ToString(context.User.FindFirst(c => c.Type == submoduleRequirement.Type.ToString()));

        if (grantedRights.Contains(submoduleRequirement.ActionType.ToString()))
        {
            context.Succeed(submoduleRequirement);
        }

        return Task.FromResult(0);
    }
}

И, наконец, класс политики:

public class SubmodulePolicy : IAuthorizationPolicyProvider
{
    public SubmodulePolicy(IOptions<AuthorizationOptions> options)
    {
        DefaultPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }

    public DefaultAuthorizationPolicyProvider DefaultPolicyProvider { get; }  

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        var submoduleTypeAndAction = policyName.Split(";");
        var submoduleTypeString = submoduleTypeAndAction[0];
        var actionTypeString = submoduleTypeAndAction[1];

        var submoduleTypeParsed = System.Enum.TryParse(submoduleTypeString, out SubmoduleType submoduleType);
        var actionTypeParsed = System.Enum.TryParse(actionTypeString, out SubmoduleActionType submoduleActionType);

        if (actionTypeParsed && submoduleTypeParsed)
        {
            var policy = new AuthorizationPolicyBuilder();
            policy.AddRequirements(new SubmoduleTypeRequirement(submoduleActionType, submoduleType));
            return Task.FromResult(policy.Build());
        }

        if (!actionTypeParsed || !submoduleTypeParsed)
        {
            throw new ArgumentException("Cannot parse action or submoduleType from Policy");
        }

        return DefaultPolicyProvider.GetPolicyAsync(policyName);
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return DefaultPolicyProvider.GetDefaultPolicyAsync();
    }
}

Проблема в том, что мой код никогда не будет доступен для моего GetPolicy. Это всегда идет к методу GetDefaultPolicyAsync. Даже атрибут не уволен. Должен ли я где-нибудь изменить пользовательский атрибут? Я сомневаюсь, что. Все, что я нахожу в Интернете, даже в официальных документах, всегда с одним аргументом, но мне нужно два из них.

Да, и вот моя конфигурация в классе запуска

        services.AddTransient<IAuthorizationPolicyProvider, SubmodulePolicy>();
        services.AddSingleton<IAuthorizationHandler, SubmoduleAuthorizationHandler>();
     services.AddAuthorization();

Есть идеи, что случилось? Есть ли ограничение только для одного аргумента? Также я думаю о замене этого фильтра.

РЕДАКТИРОВАТЬ:

Как и просил мой класс запуска. Просто чтобы прокомментировать - конфигурация jwt отключена, так как у меня была проблема с получением файла json с константами (мне было предложено ввести имя пользователя и пароль при получении файла - сервер iis). Этот веб-интерфейс в сочетании с угловой 2+ (v7)

Мой класс запуска:

   public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }
    public IContainer ApplicationContainer { get; private set; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(IISDefaults.AuthenticationScheme);

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());

        services.AddTransient<IAuthorizationPolicyProvider, SubmodulePolicy>();
        services.AddSingleton<IAuthorizationHandler, SubmoduleAuthorizationHandler>();

        //var appSettingsSection = Configuration.GetSection("Settings");
        //services.Configure<AppSettings>(appSettingsSection);
        //var appSettings = appSettingsSection.Get<AppSettings>();
        //var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        //services
        //    .AddAuthentication(auth =>
        //    {
        //        auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        //        auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        //    })
        //    .AddJwtBearer(jwtBearer =>
        //    {
        //        jwtBearer.RequireHttpsMetadata = false;
        //        jwtBearer.SaveToken = true;
        //        jwtBearer.TokenValidationParameters = new TokenValidationParameters
        //        {
        //            ValidateIssuerSigningKey = true,
        //            IssuerSigningKey = new SymmetricSecurityKey(key),
        //            ValidateIssuer = false,
        //            ValidateAudience = false
        //        };
        //    });

        services.AddAuthorization();

        services.Configure<IISServerOptions>(options =>
        {
            options.AutomaticAuthentication = true;
        });

        services.AddHttpContextAccessor();

        var container = new ContainerBuilder();

        container.RegisterType<PermissionsClient>()
            .As<IPermissionsClient>()
            .WithParameter("baseServiceUrl", Configuration.GetSection("Settings")["BaseApiUrl"])
            .WithParameter("clientCertThumbprint", Configuration.GetSection("Settings")["BaseApiClientThumbPrint"])
            .SingleInstance();

        container.RegisterType<LoginClient>()
            .As<ILoginClient>()
            .WithParameter("baseServiceUrl", Configuration.GetSection("Settings")["BaseApiUrl"])
            .WithParameter("clientCertThumbprint", Configuration.GetSection("Settings")["BaseApiClientThumbPrint"])
            .SingleInstance();

        container.RegisterType<UserClient>()
            .As<IUserClient>()
            .WithParameter("baseServiceUrl", Configuration.GetSection("Settings")["BaseApiUrl"])
            .WithParameter("clientCertThumbprint", Configuration.GetSection("Settings")["BaseApiClientThumbPrint"])
            .SingleInstance();

        container.RegisterType<SenderClient>()
            .As<ISenderClient>()
            .WithParameter("baseServiceUrl", Configuration.GetSection("Settings")["BaseApiUrl"])
            .WithParameter("clientCertThumbprint", Configuration.GetSection("Settings")["BaseApiClientThumbPrint"])
            .SingleInstance();

        container.Populate(services);
        ApplicationContainer = container.Build();

        return new AutofacServiceProvider(ApplicationContainer);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAuthentication();
        app.UseMvc();
    }
}

1 Ответ

0 голосов
/ 04 июня 2019

Мне удалось заставить это работать.Проблема была в атрибуте, который я создал.Это странная документация, потому что здесь https://docs.microsoft.com/pl-pl/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-2.2

В любом случае, мне кажется, что parrt, имя политики которого задано в установщике свойства, теперь работает для меня.Я не знаю почему.После того, как я обнаружил (хотя это очевидно, когда вы что-то наследуете), что помимо моих пользовательских параметров я могу передать также имя политики, которую я попытался проверить еще раз.И когда я жестко закодировал имя политики в свой пользовательский атрибут, внезапно наступила точка останова в методе GetPolicyAsync.Итак, я попробовал что-то в этом роде

public class CustomAuthorize : AuthorizeAttribute
{
  private const string _policyName = "SubmodulePolicy"
  private SubmoduleActionType _submoduleActionType;
  private SubmoduleType _submoduleType;

  public SubmoduleActionType ActionType;

  public SubmoduleType Type { get; set;}

  public CustomAuthorize(SubmoduleActionType submoduleActionType, SubmoduleType submoduleType)
  {
    _submoduleActionType = submoduleActionType;
    _submoduleType = submoduleType;
    Policy = $"{_policyName}-{submoduleActionType.ToString()}-{submoduleType.ToString()}"
}
}

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

public class SubmoduleAuthorizationHandler : AuthorizationHandler<SubmoduleTypeRequirement>
 {
     protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SubmoduleTypeRequirement submoduleRequirement)
{
    if (!submoduleRequirement.ActionType.HasValue)
    {
        throw new ArgumentException("No action type provided");
    }

    if (!submoduleRequirement.Type.HasValue)
    {
        throw new ArgumentException("No submodule type provided");
    }

    if (!context.User.HasClaim(uc => uc.Type == submoduleRequirement.Type.ToString()))
    {
        context.Fail();
        return Task.FromResult(0);
    }

      var grantedRights = Convert.ToString(context.User.FindFirst(c => c.Type == submoduleRequirement.Type.ToString()));

     if (grantedRights.Contains(submoduleRequirement.ActionType.ToString()))
      {
        context.Succeed(submoduleRequirement);
      }

      return Task.FromResult(0);
  }
 }

В контексте http вы получите другой контекст, чем я обновляю в методе входа в систему.Поэтому мне пришлось использовать это Добавить пользовательские утверждения для идентификации при использовании аутентификации Windows и заполнить здесь утверждения вместо метода входа в систему.Последнее может быть очевидным для многих, но это было не для меня.Возможно, мое приключение, скажем так, поможет кому-то.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...