You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C# ASP.NET MVC的多分支员工管理系统数据隔离实现问询

解决方案:实现多分支数据隔离与权限验证

你的整体思路是可行的,但当前的自定义授权属性仅完成了登录状态和ChiNhanhId有效性校验,未关联当前用户的分支权限,也未处理数据层面的过滤逻辑。以下是具体落地步骤:

1. 修正授权逻辑:绑定用户与分支ID

不要依赖Session存储ChiNhanhId,建议在用户登录时将ChiNhanhId存入身份认证Claims,这样更安全且与用户身份强绑定:

// 登录时的核心代码示例
var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.UserName),
    new Claim("ChiNhanhId", user.ChiNhanhId.ToString()),
    new Claim(ClaimTypes.Role, user.RoleName)
};
var identity = new ClaimsIdentity(claims, "ApplicationCookie");
HttpContext.GetOwinContext().Authentication.SignIn(
    new AuthenticationProperties { IsPersistent = rememberMe }, 
    identity
);

随后修改自定义授权属性,从Claims中读取ChiNhanhId,并验证当前用户与分支的归属关系:

public class CustomAuthorizeWithChiNhanhAttribute : AuthorizeAttribute
{
    private Db db = new Db();

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var user = httpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            Debug.WriteLine("[CustomAuthorize] 用户未登录");
            return false;
        }

        // 从Claims获取当前用户的ChiNhanhId
        var chiNhanhIdClaim = user.Claims.FirstOrDefault(c => c.Type == "ChiNhanhId");
        if (chiNhanhIdClaim == null || !int.TryParse(chiNhanhIdClaim.Value, out int chiNhanhId) || chiNhanhId <= 0)
        {
            Debug.WriteLine("[CustomAuthorize] 用户分支ID无效");
            return false;
        }

        // 验证当前用户确实属于该分支(防止Claims被篡改)
        var currentUser = db.NhanViens.SingleOrDefault(nv => 
            nv.UserName == user.Identity.Name && nv.ChiNhanhId == chiNhanhId
        );
        if (currentUser == null)
        {
            Debug.WriteLine("[CustomAuthorize] 用户与分支不匹配");
            return false;
        }

        // 将分支ID存入HttpContext.Items,供后续逻辑使用
        httpContext.Items["CurrentChiNhanhId"] = chiNhanhId;
        return true;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new RedirectToRouteResult(
            new RouteValueDictionary { { "controller", "Home" }, { "action", "DangNhap" } }
        );
    }
}

2. 全局数据过滤:自动限制分支数据

为避免每个控制器重复编写Where(nv => nv.ChiNhanhId == xxx),可以通过EF的全局查询过滤实现自动数据隔离:

针对EF6:实现IDbCommandInterceptor

public class BranchDataInterceptor : IDbCommandInterceptor
{
    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        ModifyCommand(command, interceptionContext);
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        ModifyCommand(command, interceptionContext);
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        ModifyCommand(command, interceptionContext);
    }

    private void ModifyCommand(DbCommand command, DbCommandInterceptionContext interceptionContext)
    {
        var httpContext = HttpContext.Current;
        // FullAdmin不受分支限制
        if (httpContext == null || httpContext.User.IsInRole("FullAdmin"))
            return;

        if (httpContext.Items["CurrentChiNhanhId"] is int chiNhanhId && chiNhanhId > 0)
        {
            // 对指定表添加分支过滤条件
            var tablesToFilter = new[] { "NhanViens", "Orders", "Positions", "Titles" };
            foreach (var table in tablesToFilter)
            {
                if (command.CommandText.Contains(table))
                {
                    command.CommandText = command.CommandText.Replace(
                        $"FROM [{table}]",
                        $"FROM [{table}] WHERE [{table}].[ChiNhanhId] = @ChiNhanhId"
                    );
                    command.Parameters.Add(new SqlParameter("@ChiNhanhId", chiNhanhId));
                }
            }
        }
    }

    // 空实现其他接口方法
    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { }
    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
}

// 在Global.asax注册拦截器
protected void Application_Start()
{
    DbInterception.Add(new BranchDataInterceptor());
    // 其他初始化代码
}

针对EF Core:使用全局查询过滤器

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var httpContext = HttpContextAccessor.HttpContext;
    if (httpContext != null && !httpContext.User.IsInRole("FullAdmin"))
    {
        if (int.TryParse(httpContext.User.Claims.FirstOrDefault(c => c.Type == "ChiNhanhId")?.Value, out int chiNhanhId))
        {
            modelBuilder.Entity<NhanVien>().HasQueryFilter(nv => nv.ChiNhanhId == chiNhanhId);
            modelBuilder.Entity<Order>().HasQueryFilter(o => o.ChiNhanhId == chiNhanhId);
            modelBuilder.Entity<Position>().HasQueryFilter(p => p.ChiNhanhId == chiNhanhId);
            modelBuilder.Entity<Title>().HasQueryFilter(t => t.ChiNhanhId == chiNhanhId);
        }
    }
}

3. 操作级验证:防止数据篡改

即使有全局过滤,在执行编辑、删除等操作时,仍需额外验证目标数据的ChiNhanhId是否与当前用户匹配(避免用户手动修改URL参数越权访问):

// 基类控制器封装通用验证逻辑
public class BaseBranchController : Controller
{
    protected Db db = new Db();

    protected bool IsDataBelongsToCurrentBranch(int entityChiNhanhId)
    {
        if (User.IsInRole("FullAdmin"))
            return true;

        if (HttpContext.Items["CurrentChiNhanhId"] is int currentChiNhanhId)
        {
            return entityChiNhanhId == currentChiNhanhId;
        }
        return false;
    }
}

// 员工控制器示例
[CustomAuthorizeWithChiNhanh]
public class NhanVienController : BaseBranchController
{
    public ActionResult Edit(int id)
    {
        var nhanVien = db.NhanViens.Find(id);
        if (nhanVien == null || !IsDataBelongsToCurrentBranch(nhanVien.ChiNhanhId))
        {
            return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
        }
        // 后续编辑逻辑
        return View(nhanVien);
    }
}

4. 特殊处理Full Admin

在授权属性和全局过滤逻辑中,对FullAdmin角色做排除处理,确保管理员可以访问所有分支的数据。

内容的提问来源于stack exchange,提问作者Zaria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:42:18