【问题标题】:ASP.Net MVC 6 Windows Authentication Custom Authorization custom role check from databaseASP.Net MVC 6 Windows 身份验证自定义授权自定义角色检查从数据库
【发布时间】:2017-02-08 18:00:37
【问题描述】:

我想用 ASP.Net MVC 6 创建一个 Intranet 应用程序

我正在使用 Windows 身份验证,我想根据数据库表设置不同的规则

例如,如果我想限制某些用户从函数或控制器访问

    // GET: TestingAuths
    [Authorize(Roles ="administrator")]
    public IActionResult Index()
    {
        return View(_context.MyTestingAuth.ToList());
    }

如何从我的数据库角色表中检查登录用户是否具有管理员角色。 这是一个解决方案,但不适用于 ASP.Net MVC 6:http://kitsula.com/Article/Custom-Role-Provider-for-MVC

我想要 ASP.Net MVC 6 的解决方案

【问题讨论】:

    标签: authentication asp.net-core-mvc


    【解决方案1】:

    我想你现在已经完成了这项工作,但是对于我在学习 MVC 和 .Net Core 时正在开发的 Intranet,我使用了基于声明的授权,该授权依赖于对 Person 持有的数据库值。

    我是这样处理的,毫无疑问可以改进,但希望能及时实现。

    Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
     services.AddMvc(config =>
     {
        var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
        config.Filters.Add(new AuthorizeFilter(policy));
     })
    
     services.AddAuthorization(options =>
     {
        options.AddPolicy("Administrator", policy => policy.RequireClaim("Administrator"));
     });
    
     services.Configure<IISOptions>(options =>
     {
        options.ForwardWindowsAuthentication = true;
     });
    
     var connection = etc etc;
     services.AddDbContext<IntranetContext>(options => options.UseSqlServer(connection));
    
     services.AddScoped<IClaimsTransformer, ClaimsTransformer>();
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseSession();
            app.UseDefaultFiles();
            app.UseStaticFiles();
            app.UseClaimsTransformation(async (context) =>
            {
                IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
                return await transformer.TransformAsync(context);
            });
    
            app.UseStatusCodePages();
    
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    

    ClaimsTransformer.cs

    public class ClaimsTransformer : IClaimsTransformer
    {
        private readonly IntranetContext dbcontext;
    
        /// <summary>
        /// Initializes a new instance of the <see cref="ClaimsTransformer" /> class.
        /// </summary>
        /// <param name="context">Also to be written.</param>
        public ClaimsTransformer(IntranetContext context)
        {
            this.dbcontext = context;
        }
    
        /// <summary>
        /// Manages claims against the ClaimsPrincipal for Authenticated Users
        /// </summary>
        /// <param name="context">Also to be written.</param>
        /// <returns>Still to be written.</returns>
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
        {
            System.Security.Principal.WindowsIdentity windowsIdentity = null;
    
            foreach (var i in context.Principal.Identities)
            {
                if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
                {
                    windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
                }
            }
    
            if (windowsIdentity != null)
            {
                var username = windowsIdentity.Name.Remove(0, 6);
                var appUser = this.dbcontext.Person.FirstOrDefault(m => m.Username == username);
    
                if (appUser != null)
                {
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id), ClaimValueTypes.Integer));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Fullname", appUser.Firstname + ' ' + appUser.Surname, ClaimValueTypes.String));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Firstname", appUser.Firstname, ClaimValueTypes.String));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Surname", appUser.Surname, ClaimValueTypes.String));
    
                    if (appUser.Administrator)
                    {
                        ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Administrator", "1", ClaimValueTypes.Boolean));
                    }
                }
                else
                {
                    Person newPerson = new Person();
                    newPerson.Username = username;
                    newPerson.Firstname = username.Split('.')[0].ToString().ToTitleCase();
                    newPerson.Surname = username.Split('.')[1].ToString().ToTitleCase();
                    newPerson.LocationId = 1;
                    newPerson.CreatedBy = 1;
                    newPerson.CreatedDate = DateTime.Now;
                    newPerson.Email = username + "@mycompany.com";
                    this.dbcontext.Add(newPerson);
                    await this.dbcontext.SaveChangesAsync();
    
                    appUser = this.dbcontext.Person.FirstOrDefault(m => m.Username == username);
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id), ClaimValueTypes.Integer));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Fullname", appUser.Firstname + ' ' + appUser.Surname, ClaimValueTypes.String));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Firstname", appUser.Firstname, ClaimValueTypes.String));
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Surname", appUser.Surname, ClaimValueTypes.String));
                }
            }
    
            return await System.Threading.Tasks.Task.FromResult(context.Principal);
        }
    }
    

    然后我可以在任何控制器上应用 [Authorize(Policy = "Administrator")]

    我希望这对你有用。

    谢谢。

    【讨论】:

    • 一些内联 cmets 会大大改善这一点。
    • 我会尝试@Gusdor,但作为一个新手,我仍然不能完全理解这里发生的一切,而是根据 SO 和 MSDN 研究编译它,特别是 asp-net/core 站点。
    • 没有戏剧性。我花了一些时间在上面,并对关键元素有一个粗略的了解。我会尽我所能提供帮助。
    【解决方案2】:

    理想情况下,您的身份/身份验证提供者将在声明身份中提供角色声明(如果这在您的控制范围内)

    但是,另一种方法是编写自己的 AuthorizationHandler 来拦截 mvc 调用。

    public class CustomAuthorizationRequirement : AuthorizationHandler<CustomAuthorizationRequirement>, IAuthorizationRequirement
    {
        private readonly AppDBContext _context;
        public CustomAuthorizationRequirement(AppDBContext context)
        {
            _context = context;
        }
    
        protected override void Handle(AuthorizationContext context, CustomAuthorizationRequirement requirement)
        {
            var isValid = false;
    
            //perform any checks you want here i.e. check for authorization filters and validate against your database roles
            //Due to the 2 different versions of AuthorizationContext I have hard referenced this
            if(context.Resource is Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)
            {
                //Get the MVC authorization context
                var authContext = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
    
                //Find the AuthorizeAttribute from the given function
                var authAttribute = authContext.Filters.OfType<AuthorizeAttribute>().FirstOrDefault();
    
                foreach(var role in authAttribute.Roles.Split(new char[] {','}))
                {
                    var isInRole = _context.Set<ApplicationUser>().Count(u => u.UserName == context.User.Name && u.UserRoles.Contains(role) > 0);
                    if (isInRole)
                        isValid = true;
    
                }
            }
    
    
            if (isValid )
                context.Succeed(requirement);
            else
                context.Fail();
        }
    }
    

    然后注册:

    var defaultPolicy = new AuthorizationPolicyBuilder().AddRequirements(new CustomAuthorizationRequirement ()).Build();
    var mvcBuilder = services.AddMvcCore(options => options.Filters.Add(new AuthorizeFilter(defaultPolicy)));
    

    【讨论】:

    • 您能否添加更多信息我如何检查数据库中的授权过滤器或数据库中的角色
    • 你如何排序/管理你的用户/角色模型取决于你.....但我添加了一个编辑来展示一个粗略的例子
    • 你能给我一步一步的例子吗?我是新手
    • 它看起来应该对我有用,但它建立了一个 policy。所以,我认为您将使用[Authorize(Policy="administrator")] 而不是[Authorize(Roles ="administrator")]
    【解决方案3】:

    您应该查看代码中的策略,而不是进行角色检查。

    需求的处理程序可以在其构造函数中使用 DI 元素,因此您可以在 DI 中注册 RoleRepository,然后在处理程序构造函数中使用它。然后使用参数化需求来配置您的策略。

    所以,例如;

    public class MyRoleRequirement : IAuthorizationRequirement
    {
        public MyRoleRequirement(string roles)
        {
            Roles = roles;
        }
    
        public string Roles { get; set; }
    }
    

    现在,让我们假设您的角色存储库继承自 IRolesRepository 并带有 IsInRole(string roles, ClaimsPrincipal user) 函数。在你的处理程序中,你会做类似的事情

    public class MyRoleAuthorizationHandler : AuthorizationHandler<MyRoleRequirement>
    {
        IRolesRepository _rolesRepository;
    
        public MyRoleAuthorizationHandler(IRolesRepository rolesRepository)
        {
            _employeeRepository = employeeRepository;
        }
    
        protected override void Handle(AuthorizationContext context, 
                                       MyRoleRequirement requirement)
        {
            if (_rolesRepository.IsInRole(requirement.Roles, context.User)
            {
                context.Succeed(requirement);
            }
        }
    }
    

    然后,您只需在 ConfigureServices() 中的 startup.cs 中配置您的策略和处理程序,并记住在 DI 系统中注册您的角色存储库,然后它就会消失。

    可能看起来像

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Administrators", policy =>
        { 
            policy.Requirements.Add(new MyRolesRequirement("Administrator));
        });
    });
    
    services.AddSingleton<IAuthorizationHandler, MyRolesAuthorizationHandler>();
    

    Code based policiesDI in handlers 一样在文档中

    请注意,这段代码是在输入框中完成的,而不是 VS,所以它可能无法编译:)

    【讨论】:

    • 亲爱的 Blowdart,你能不能给我一步一步的详细例子,因为我是新手
    • 嗯,就目前而言它是正确的,但是 (a) 它无法解释 为什么 你不应该进行角色检查 [真的,为什么我们_拥有角色并且没有明显的修改它们的方法?]_,并且(b)它使用MyRoleMyRoles不一致,所以它显然不是实际测试的代码。 @blowdart 在其他地方已经更好地解释了这一切,但我花了好几天才找到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多