程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 嘗試asp.net mvc 基於controller action 方式權限控制方案可行性,asp.netmvc

嘗試asp.net mvc 基於controller action 方式權限控制方案可行性,asp.netmvc

編輯:關於.NET

嘗試asp.net mvc 基於controller action 方式權限控制方案可行性,asp.netmvc


微軟在推出mvc框架不久,短短幾年裡,版本更新之快,真是大快人心,微軟在這種優秀的框架上做了大量的精力投入,是值得贊同的,畢竟程序員駕馭在這種框架上,能夠強力的精化代碼,代碼層次也更加優雅,擴展較為方便,使之程序員把更多的精力投入到業務中來。

很多時候我就在想,是不是該把傳統的用戶權限管理換個方式了呢?換成MVC AOP的思想權限控制,諸如controller action這種方案行的通嗎?答案是肯定的,人的思想永遠是第一位!

看看我們想要達到的效果

1)權限列表 

 

2)菜單權限列表

 

3)角色權限列表

4)用戶權限列表

5)主從菜單的配置

 

6)圖標的自由定制

 

1.基於controller action控制權限的好處

其實合起來看,controller與action即控制了一個頁面的行為,如能是否查看,寫入,修改權限,而我們在開發的過程中,這些方法都已完成,這樣省去了像傳統方式對每個頁面重新控制生成的步驟。當前通過反射程序集收集所有的controller action信息,自動化收集權限控制是比較可觀的。

2.基於controller action控制權限的方案

權限列表通過反射自動從程序集獲取,管理員(默認有最高權限)把權限分配給用戶及分配權限分配給角色,管理員對用戶分配權限角色權限,如圖所示

 

用戶權限列表及角色權限列表分配成形Controller與Action信息後,通過代碼控制對應的控制器及方法是否有權限。

 

3.基於controller action控制權限信息的提取的方案

有些方法不需要提取的,有些方法需要權限控制,為了讓程序方便提取權限信息,我們加入特性,如果方法或控制器有此特性,即要控制的,當然為了節約代碼,默認特性是false,這樣,沒有加特性的或者特性是false的,都不用提取!

 

4.基於mvc controller和action 權限管理流程圖

 

不明白的親們不用太著急,下面開始詳細的步驟吧!

 

首先我們通過上面的分析,我們用模型來一點一點的剖析

從左至右,相關的模型是,權限信息列表,角色列表,用戶信息列表,部門列表,菜單列表

1)一個用戶可以有多個權限,一個權限可以分配多個用戶,所以是多對多的關系

2)一個角色可以有多個權限,一個權限可以分配多個角色,所以是多對多的關系

3)一個用戶可以有多個角色,一個角色可以分配多個用戶,所以是多對多的關系

4)一個用戶可以有多個部門,一個部門可以分配多個角戶,所以是多對多的關系

5)菜單列表,主要針對後台每個用戶或角色不同的展示方式,以及可以自定義圖片等

 

好了,到此為止,我們開始正式的工作。

由上列模型,我們采用code first生成數據庫,如何使用 code first 請搜索下百度或將來有專門的章節介紹,這裡不再累贅說明!

 

我們添加,添加以後幾個model類

 

 1)權限控制類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WL.Models.Permission
{
    [DisplayName("控制權限")]
    public class ActionPermission
    {
        [Key]
        [DisplayName("控制權限ID")]
        public int ActionPermissonID { set; get; }

        [DisplayName("控制權限名稱")]
        public string ActionPermissionName { set; get; }

        [DisplayName("控制器權限名稱")]
        public string ControllerPermissionName { set; get; }

        [DisplayName("說明")]
        public string Description { set; get; }

        [DisplayName("創建時間")]
        public DateTime CreateDate { set; get; }

        [DisplayName("操作用戶名")]
        public string Operator { set; get; }

        [DisplayName("最後修改時間")]
        public DateTime LateDate { set; get; }

        [DisplayName("圖標")]
        public string Icon { get; set; }

        [DisplayName("狀態")]
        public int State { set; get; }

        [Description("用戶實體集合")]
        public virtual ICollection<User> UserCollection { get; set; }

        [Description("角色實體")]
        public virtual ICollection<Role> RoleCollection { get; set; }

    }
}

 

  2)部門類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WL.Models.Permission
{
    [DisplayName("部門")]
    [DisplayColumn("DepartMentID")]
    public class DepartMent
    {
        [Key]
        [DisplayName("部門ID")]
        public int DepartMentID { set; get; }

        [DisplayName("部門名稱")]
        public string DepartName { set; get; }

        [DisplayName("說明")]
        public string Description { set; get; }

        [DisplayName("創建時間")]
        public DateTime CreateDate { set; get; }

        [DisplayName("操作用戶名")]
        public string Operator { set; get; }

        [DisplayName("最後修改時間")]
        public DateTime LateDate { set; get; }

        [DisplayName("圖標")]
        public string Icon { get; set; }


        [DisplayName("狀態")]
        public int State { set; get; }

        [Description("用戶實體集合")]
        public virtual ICollection<User> UserCollection { get; set; }

    }
}

 

   3)菜單類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WL.Models.Permission
{
    [DisplayName("菜單")]
    [DisplayColumn("MenuID")]
    public class Menu
    {
        [Key]
        [DisplayName("菜單ID")]
        public int MenuID { set; get; }

        [DisplayName("ParentID")]
        public int ParentsID { set; get; }

        [DisplayName("菜單名稱")]
        public string MenuName { set; get; }

        [DisplayName("菜單圖標")]
        public string MenuIco { set; get; }

        [DisplayName("說明")]
        public string Descriptotion { set; get; }


        [DisplayName("控制權限名稱")]
        public string ActionPermissionName { set; get; }

        [DisplayName("控制器權限名稱")]
        public string ControllerPermissionName { set; get; }

        [DisplayName("鏈接地址")]
        public string Url { set; get; }

        [DisplayName("排序")]
        public string Sort { set; get; }

        [DisplayName("創建時間")]
        public DateTime CreateDate { set; get; }

        [DisplayName("操作用戶名")]
        public string Operator { set; get; }

        [DisplayName("最後修改時間")]
        public DateTime LateDate { set; get; }

        [DisplayName("圖標")]
        public string Icon { get; set; }


        [DisplayName("狀態")]
        public int State { set; get; }

    }
}

 

4)角色類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace WL.Models.Permission
{
    [DisplayName("角色成員")]
    [DisplayColumn("RoleID")]
    public class Role
    {
        [Key]
        [DisplayName("用戶ID")]
        public int RoleID { set; get; }

        [DisplayName("角色名")]
        public string RoleName { set; get; }

        [DisplayName("說明")]
        public string Description { set; get; }

        [DisplayName("創建時間")]
        public DateTime CreateDate { set; get; }

        [DisplayName("操作用戶名")]
        public string Operator { set; get; }

        [DisplayName("最後修改時間")]
        public DateTime LateDate { set; get; }

        [DisplayName("圖標")]
        public string Icon { get; set; }

        [DisplayName("狀態")]
        public int State { set; get; }

        [Description("用戶實體集合")]
        public virtual ICollection<User> UserCollection { get; set; }

        [Description("控制權限實體集合")]
        public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }

    }
}

 

5)用戶後台管理員類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace WL.Models.Permission
{
    [DisplayName("用戶成員")]
    [DisplayColumn("UserId")]
    public class User
    {
        [Key]
        [DisplayName("用戶ID")]
        public int UserID { set; get; }

        [DisplayName("用戶名稱")]
        public string UserName { set; get; }

        [DisplayName("用戶密碼")]
        public string PassWord { set; get; }

        [DisplayName("用戶郵件")]
        public string Mail { set; get; }

        [DisplayName("用戶電話")]
        public string Phone { set; get; }

        [DisplayName("說明")]
        public string Description { set; get; }

        [DisplayName("創建時間")]
        public DateTime CreateDate { set; get; }

        [DisplayName("操作用戶名")]
        public string Operator { set; get; }

        [DisplayName("最後修改時間")]
        public DateTime LateDate { set; get; }

        [DisplayName("圖標")]
        public string Icon { get; set; }

        [DisplayName("狀態")]
        public int State { set; get; }

        [Description("部門實體")]
        public virtual ICollection<DepartMent> DepartMentCollection { get; set; }

        [Description("角色實體")]
        public virtual ICollection<Role> RoleCollection { get; set; }

        [Description("控制權限實體集合")]
        public virtual ICollection<ActionPermission> ActionPermissionCollection { get; set; }

    }
}

 

在項目中任意添加一個項目 MvsApp->PermissionContext

 

並配置web.config

 

在此項目中設為啟始項目 然後台下操作

設置默認項目

 

在命令行裡輸入

Enable-Migrations-->Add-Migration Rating-->update-database 

在數據中將會生成權限數據庫

 

我們采用code first好處在於,“EF”自動為我們創建中間關系表,省去了中間我們手工創建的省麻煩。如果不清楚的朋友,請查EF Code first 自動遷移相關文章。

然後,我們布局各個控制器如下圖

可以看出,任何一個權限控制器類都所繼續一個基類baseController,這樣寫的好處只有一個,節約代碼,因為‘懶’。

在基baseControlle類中我們將為權限控制進行描述。

基於AOP的思想,在此基類baseControlle中,重寫 Controller的 OnActionExecuting()方法,來判斷用戶的相關權限,大致邏緝流程如下

 

通過上面的流程的分析,代碼如下

1)判斷用否是否成功登錄

  #region -----校驗用戶是否登錄進入網站的-----
            base.OnActionExecuting(filterContext);

            HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");

            //檢驗用戶是否已經登錄,如果登錄則不執行,否則則執行下面的跳轉代碼
            if (UserInfo == null)
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }
            #endregion

            var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();

            if (string.IsNullOrEmpty(UserInfoStr))
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }

 

2)提取登錄用戶相關登錄的信息(注冊這裡是後台相關演示,安全性沒做處理)

   var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
            var username = userNameArray[0];
            var pwdword = userNameArray[1];

            CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();

            if (CurrentUserInfo == null)
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }

 

3)獲取當前用戶所在的控制器與方法並根據權限特性(上文提到的自定義判斷權限依據)判斷當前控制器是否需要權限控制

   //先將當前的請求,到權限表裡面去找對應的數據
            System.Web.Routing.RouteData Rotedate = filterContext.RequestContext.RouteData;
            string controller = (RouteData.Values["controller"] ?? "").ToString().ToLower();
            string action = (RouteData.Values["action"] ?? "").ToString().ToLower();

            //默認
            if (HasDefaultAction(filterContext))
                return;
   private bool HasDefaultAction(ActionExecutingContext filterContext)
        {
            if (CurrentUserInfo.UserName == "admin")
                return true;

            Type t = filterContext.ActionDescriptor.ControllerDescriptor.ControllerType;
            string actionname = filterContext.RouteData.Values["action"].ToString();
            //默認沒用應用權限特性,僅不需要權限的才進行控件該特性,如果應用,HasPermission必須false才可以進行放行些權限
            object[] astri = GetPermissionAttribute<PermissionAttribute>(actionname, t);
            if (astri.Length > 0)
            {
                //更具自定義的特性得到需要調用的類名與方法名
                PermissionAttribute u = astri[0] as PermissionAttribute;
                return !u.RequiredPermission;
            }
            return false;
        }

 

4)至此用戶程序都通過後,開始用戶相關的權限判斷。有兩條線路判斷,一是角戶權限判斷,二是用戶權限判斷。

角戶權限判斷

            //然後和權限表進行對比,如果取出來則通過請求,否則不通過
            //取出當前權限的數據
            //想去用戶權限表裡面查詢有沒有數據
            //分析線路 User->Role->Action
            //拿到當前的用戶信息
            var userCurrent = userInfoService.LoadEntites(u => u.UserID == CurrentUserInfo.UserID).FirstOrDefault();
            var role = (from r in userCurrent.RoleCollection
                        from c in r.ActionPermissionCollection
                        where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                        select r).FirstOrDefault();
            if (role != null)
                return;

 

用戶權限(後台用戶)判斷

            //分析線路 User->Action
            var user = (from c in userCurrent.ActionPermissionCollection
                        where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                        select c).FirstOrDefault();

            if (user != null)
                return;

 

其它可能的錯誤

        public void EndRequest(ActionExecutingContext filterContext)
        {
            filterContext.Result = RedirectToRoute(new { Controller = "Home", Action = "Error" });
        }

 

至此,一個基本的用戶控制已經完成

完整的代碼:

            #region -----校驗用戶是否登錄進入網站的-----
            base.OnActionExecuting(filterContext);

            HttpCookie UserInfo = System.Web.HttpContext.Current.Request.Cookies.Get("COOKIE_NAME_FOR_USER");

            //檢驗用戶是否已經登錄,如果登錄則不執行,否則則執行下面的跳轉代碼
            if (UserInfo == null)
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }
            #endregion

            var UserInfoStr = UserInfo["COOKIE_NAME_FOR_USER_INFO"].ToString();

            if (string.IsNullOrEmpty(UserInfoStr))
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }

            var userNameArray = UserInfoStr.Split(new string[] { "$|$" }, StringSplitOptions.RemoveEmptyEntries);
            var username = userNameArray[0];
            var pwdword = userNameArray[1];

            CurrentUserInfo = this.userInfoService.LoadEntites(u => username.Equals(u.UserName) && pwdword.ToLower().Equals(u.PassWord.ToLower()) && u.State == 0).FirstOrDefault();

            if (CurrentUserInfo == null)
            {
                filterContext.Result = RedirectToRoute(new { Controller = "Login", Action = "Index" });
                return;
            }

            #region -------檢驗用戶是否有訪問此地址的權利----
            //先將當前的請求,到權限表裡面去找對應的數據
            System.Web.Routing.RouteData Rotedate = filterContext.RequestContext.RouteData;
            string controller = (RouteData.Values["controller"] ?? "").ToString().ToLower();
            string action = (RouteData.Values["action"] ?? "").ToString().ToLower();

            //默認
            if (HasDefaultAction(filterContext))
                return;

            //然後和權限表進行對比,如果取出來則通過請求,否則不通過
            //取出當前權限的數據
            //想去用戶權限表裡面查詢有沒有數據
            //分析線路 User->Role->Action
            //拿到當前的用戶信息
            var userCurrent = userInfoService.LoadEntites(u => u.UserID == CurrentUserInfo.UserID).FirstOrDefault();
            var role = (from r in userCurrent.RoleCollection
                        from c in r.ActionPermissionCollection
                        where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                        select r).FirstOrDefault();
            if (role != null)
                return;

            //分析線路 User->Action
            var user = (from c in userCurrent.ActionPermissionCollection
                        where c.ActionPermissionName.ToLower() == action && c.ControllerPermissionName.ToLower() == controller && c.State == 0
                        select c).FirstOrDefault();

            if (user != null)
                return;

            EndRequest(filterContext);

 

到此,可以看到任何一個控制器都將受到這個基控制器的‘影響’,為此我們沒必要去一個一個的控制器類實現,AOP的思想,確實為我們節約了不少代碼。

簡單的總結下思路:

用戶登錄後寫入cookie ,在此基類中,讀取此cookie存儲的相關用戶相關信息,如用戶名等,同時讀取當前訪問的mvc相關的controller action用戶和角色相關的權限信息,

如果用戶有此controller action權利,那麼我們即可放行

為了節約數據庫的資源,在此基礎加入判斷,當前訪問的controller action 與用戶當前操作的controller action 是否控制特性判斷,如果在些方法上不存在權限控制的特性或者為false,那麼,說明不需要權限控制,直接放行。

 

本來想在一篇文章寫完所有的權限內容的,寫到此,文章太夠長了,只能期待下篇。

下篇文章我們主要寫各種控制器的具體控制,文章很長,請細細品嘗哦。

 

作者:谷歌's(谷歌's博客園)
出處:http://www.cnblogs.com/laogu2/ 歡迎轉載,但任何轉載必須保留完整文章,在顯要地方顯示署名以及原文鏈接。如您有任何疑問或者授權方面的協商,請給我留言。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved