ASP.NET MVC实现权限管理以及权限分配

最近在写单位项目时,需要写一个权限管理,就是不同的用户对应的权限需要不同。第一次接触这方面的内容,在搜集资料和实践后,总结一下开发经验。按照先总后分的顺序来记录:

总的思路

正常的RABC包括三个角色:用户、角色以及权限。用户跟角色对应,角色与权限对应。但是,我的项目并没有这么复杂的需求,因此降低了难度,只做了用户以及对应的权限。相应的表格,就增加了三个:User,Permission以及UserPermission。表格部分可以参考这篇博文:

https://blog.csdn.net/qq_33285360/article/details/129837803?spm=1001.2014.3001.5502

然后是C#部分的代码,这里主要是利用过滤器的技术。过滤器的相关内容这里不赘述,可以自己翻阅一下资料,我这里用的是ActionFilterAttribute。C#部分的代码我认为主要有三个方面:第一个是过滤器的内容,第二个是过滤器的注册,第三个是登录部分的代码。下面对具体的代码进行记录,由于表格比较简单,参考那篇文章即可,下面主要对C#部分代码进行分析:

代码部分详解:

1.过滤器内容

我先贴代码

public class UserPermissionAttribute : ActionFilterAttribute
{
    // 数据库连接字符串(实际项目中放在配置文件)
    string MyConn = "server=.;database=didi;Trusted_Connection=true;Max Pool Size=500";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        // 关键:检查当前请求是否有[AllowAnonymous]特性,有则直接跳过验证
        var allowAnonymous = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
                            || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);
        if (allowAnonymous)
        {
            base.OnActionExecuting(filterContext);
            return; // 跳过后续验证
        }
        
        // 获取当前用户身份
        var user = filterContext.HttpContext.User.Identity;
        Debug.WriteLine("用户是否登录:"+user.IsAuthenticated+"用户名:"+user.Name);
        
        // 未登录用户拒绝访问
        if (!user.IsAuthenticated)
        {
            filterContext.Result = new HttpUnauthorizedResult();
            return;
        }

        // 构建当前操作所需权限标识(控制器名.动作名)
        var requiredPermission = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName + "." +
                                filterContext.ActionDescriptor.ActionName;
        Debug.WriteLine("需要的权限"+requiredPermission);

        // 检查用户是否直接拥有该权限
        if (!UserHasPermission(user.Name, requiredPermission))
        {
            filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Forbidden);
            return;
        }
        //这句话表示该过滤器是在动作方法前执行
        base.OnActionExecuting(filterContext);
    }

    /// <summary>
    /// 检查用户是否直接拥有指定权限
    /// </summary>
    private bool UserHasPermission(string userName, string permissionName)
    {
        using (var connection = new SqlConnection(MyConn))
        {
            // SQL查询:通过用户名关联用户表和用户权限表,检查是否存在匹配权限
            string query = "SELECT COUNT(*) FROM UserPermissions up INNER JOIN Users u ON up.UserId = u.Id INNER JOIN Permissions p ON up.PermissionId = p.PermissionId WHERE u.UserName = @UserName AND p.PermissionName = @PermissionName";

            using (var command = new SqlCommand(query, connection))
            {
                // 添加参数防止SQL注入
                command.Parameters.AddWithValue("@UserName", userName);
                command.Parameters.AddWithValue("@PermissionName", permissionName);
                
                connection.Open();
                // 执行查询并返回结果(大于0表示有权限)
                var result=Convert.ToInt32(command.ExecuteScalar());
                Debug.WriteLine("查询结果"+result);
                return result > 0;
            }
        }
    }
}

核心部分是函数UserHasPermission,当用户登录以后,会查询数据库。传入的参数中,user.Name是用户登录时输入的名字,而requiredPermission是controller.action的名字,通过对比数据库中UserPermission的内容,如果有,则符合权限,true,如果没有,则是false。由于将过滤器UserPermissionAttribute设置为全局,因此还有一小部分代码是用于登录时的[allowAnonymous]。

2.过滤器的注册

namespace qbxt
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new UserPermissionAttribute());
            filters.Add(new HandleErrorAttribute());
        }
    }
}

这里不做赘述,设为了全局代码

3.登录界面的后端

namespace didi.Controllers
{
    public class LoginController : Controller
    {
        // GET: /Login/
        [AllowAnonymous] // 允许匿名访问登录页
        public ActionResult Index()
        {
            // 如果已经登录,直接跳转到首页
            if (User.Identity.IsAuthenticated)
            {
                return Redirect("/one_leida/Index");
            }
            return View();
        }

        // POST: /Login/Login (建议使用HttpPost特性限定提交方式)
        //[HttpPost]
        
        //[ValidateAntiForgeryToken] // 防止CSRF攻击
        [AllowAnonymous]
        public ActionResult Login(string username, string password)
        {
            // 基本参数验证
            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            {
                ViewBag.error = "用户名和密码不能为空!";
                return View();
            }

            string connectionString = "server=.;database=qbxt;Trusted_Connection=true;Max Pool Size=500";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                try
                {
                    connection.Open();
                    
                    string query = "SELECT * FROM [user] WHERE username = @Username AND password = @Password";

                    using (SqlCommand command = new SqlCommand(query, connection))
                    {
                        command.Parameters.AddWithValue("@Username", username);
                        command.Parameters.AddWithValue("@Password", password); // 注意:实际项目需加密存储密码

                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                // 关键修复:创建用户认证票据,标记用户为已登录
                                // 参数1:用户名;参数2:是否记住登录状态
                                FormsAuthentication.SetAuthCookie(username, createPersistentCookie: false);

                                // 可选:记录用户ID到Session(如果后续需要)
                                Session["userId"] = reader["Id"];

                                // 登录成功跳转
                                return Redirect("/one_leida/Index");
                            }
                            else
                            {
                                ViewBag.error = "用户名或密码错误!";
                            }
                        }
                    }
                }
                catch (SqlException ex)
                {
                    // 数据库异常处理(避免暴露敏感信息)
                    ViewBag.error = "数据库访问错误,请稍后重试";
                    // 实际项目中应记录详细日志
                    // Logger.Error("登录数据库错误", ex);
                }
                catch (Exception ex)
                {
                    ViewBag.error = "系统错误,请稍后重试";
                    // Logger.Error("登录系统错误", ex);
                }
            }

            return View();
        }

        // 退出登录功能
        public ActionResult Logout()
        {
            // 清除认证状态
            FormsAuthentication.SignOut();
            // 清除Session
            Session.Abandon();
            // 重定向到登录页
            return RedirectToAction("Index");
        }
    }
}

核心部分就是:public ActionResult Login(string username, string password)
从登录界面的前端获取用户名和密码,这里有个关键语句:

FormsAuthentication.SetAuthCookie(username, createPersistentCookie: false);

用于记录登录状态。并且记录了用户信息,为后面过滤器验证时提供了验证信息。值得注意的是,如果要使用FormsAuthentication,需要先注册。下面是web.config部分的注册内容:

<system.web>    
   <authentication mode="Forms">
		      <forms
			  loginUrl="~/Login/Index"
			  timeout="2880"           
		      name=".ASPXAUTH"          
		      path="/"
		      requireSSL="false"
		      slidingExpiration="true" />
   </authentication>
</system.web>

以上是实现该功能的一个大致思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值