【问题标题】:ASP.NET Core Custom Authorize AttributeASP.NET Core 自定义授权属性
【发布时间】:2019-05-30 13:08:44
【问题描述】:

我有这种自定义授权配置。问题是我想检查子模块以及它是否有权限。我还有一个登录控制器,我在其中构建声明等,并添加它们。但我认为我在这里也遗漏了一些东西。我想我应该向当前负责人添加声明,但我不知道如何:

这是我在登录控制器中的登录操作

[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);
    }

客户授权

    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 服务器)。这个 web api 结合了 angular 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();
    }
}

【问题讨论】:

  • 你能展示你的startup.cs吗?你在那里设置了什么身份验证?饼干?智威汤逊?
  • 我已经编辑了我的问题,并添加了启动类
  • 如果您没有进行身份验证,授权将无法正常工作。当我不知道你是谁时,我怎么知道你可以做什么?
  • 但我有类似的东西 services.AddAuthentication(IISDefaults.AuthenticationScheme);这还不够吗?

标签: c# asp.net-core-webapi


【解决方案1】:

我已经设法让它工作了。问题出在我创建的属性中。这很奇怪,因为文档在这里https://docs.microsoft.com/pl-pl/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-2.2

无论如何,在属性的 setter 中设置策略名称的 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 上下文中,您将获得不同的上下文,然后我在登录方法中进行更新。所以我不得不使用这个Add custom claims to identity when using Windows authentication 并在这里填充声明而不是登录方法。最后一个对很多人来说可能很明显,但它不适合我。也许我的假设也是冒险会帮助某人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-12
    • 2021-03-17
    • 2022-08-19
    • 1970-01-01
    • 2020-04-11
    • 2019-07-31
    • 2012-05-09
    • 1970-01-01
    相关资源
    最近更新 更多