Asp.net MVC中的全局權限驗證方法及實現

       開發過MVC的人都知道,MVC是三個單詞的縮寫,分別爲: 模型(Model),視圖(View)和控制Controller)。 MVC模式的目的就是實現Web系統的職能分工。 Model層實現系統中的業務邏輯。 View層用於與用戶的交互。 Controller層是Model與View之間溝通的橋樑,它可以分派用戶的請求並選擇恰當的視圖以用於顯示,同時它也可以解釋用戶的輸入並將它們映射爲模型層可執行的操作。

        我們的權限驗證就添加在控制(Controller)層,這是必然的。驗證的機制就是,每一次客戶端對於Controller裏邊的Action的請求之前,都會先驗證用戶的身份,也就是說,用戶每一次對於數據的請求,都會驗證當前用戶是否有使用該數據的權限。

本文所介紹的MVC身份驗證大致分爲以下四個部分:

AuthenticationAsserts         授權驗證的相關斷言

AuthorizationFilter               驗證過濾器的構造函數

ControllerActionInvoker      自定義ControllerActionInvoker

DefaultControllerFactory     自定義的ControllerFactory,可以通過ControllerFactory向Controller中植入相關的ActionInvoker

下邊先從授權驗證的相關斷言說起,驗證斷言的作用是:確定用戶的請求是否需要驗證和確認是否以驗證過。它主要包含以下幾個方法:精確的權驗驗證,確定系統是否需要驗證,確定用戶是否已經通過驗證,以及判斷用戶是否擁有訪問該URL的權限。下邊看看代碼:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Configuration;
using Membership;
using System.Web;
using User.ServiceImp;
using MVCExtension;

namespace Authorization
{
    /// <summary>
    /// 授權驗證的相關斷言。
    /// </summary>
    public class AuthenticationAsserts
    {
        /// <summary>
        /// 確認系統是否需要權限驗證(系統在開發期間,爲了開發調試方便,可以不需要權限驗證,而在開發完成後,套用框架後纔要求驗證)。
        /// </summary>
        /// <returns>如果需要驗證,則返回True,否則返回False</returns>
        public static bool IsRequireAuthenticate()
        {
            try
            {
             return  bool.Parse(ConfigConstString.Instance.IsSOAVlidate);
            }
            catch
            {
                return false;
            }
        }
        
        /// <summary>
        /// 判斷用戶是否已經通過登錄驗證
        /// </summary>
        /// <param name="controllerContext">Controller的上下文</param>
        /// <returns>如果通過驗證,返回True,否則返回False</returns>
        public static bool IsAuthenticated(ControllerContext controllerContext)
        {
            return controllerContext.HttpContext.User.Identity.IsAuthenticated;
        }
        
        /// <summary>
        /// 判斷Action是否標示爲避免驗證?(AvoidAuthenticateAttribute)
        /// </summary>
        /// <returns>如果該Action標示爲避免驗證,返回True,否則返回False。</returns>
        public static bool IsActionAvoidAuthenticate(ControllerContext   Controller,   IList<IAuthorizationFilter> filters)
        {
            bool  _ISSecure =false;
            foreach (IAuthorizationFilter filer in filters)
            {
                if (filer is SecureFilterAttribute)
                {
                    SecureFilterAttribute secureFilterAttribute = (SecureFilterAttribute)filer;
                    _ISSecure = (bool)secureFilterAttribute.ISSecure;
                    if (_ISSecure) break;
                }
                else if (filer is ElinkFilterAttribute)
                {
                    string controller = Controller.Controller.ToString();

                    string id = HttpContext.Current.Request.Params["ElinkID"];
                    try
                    {
                        Guid linkid = id == null ? Guid.NewGuid() : new Guid(id);
                        _ISSecure =UserGateway.TenantProvider.IsLinkerSecure(controller, linkid,"");
                                           
                    }
                    catch(Exception e)
                    {
                        _ISSecure = false;
                    }
                  
                break;
                }
                else if(filer is AccurateFilterAttribute)//鏈接授權
                {
                    Identity bi = (Identity)Controller.HttpContext.User.Identity;
                    int userid = bi.UserID;
                    HttpRequest httpRequest = new HttpRequest(string.Empty, Controller.HttpContext.Request.Url.ToString(), Controller.HttpContext.Request.QueryString.ToString());
                    bool result = UserGateway.TenantSecurityProvider.IsSignatureUrlsecure(httpRequest,userid);
                    _ISSecure = result;

                }

            }
            return _ISSecure;
        }
        /// <summary>
        /// 精確授權驗證
        /// </summary>
        /// <param name="Controller"></param>
        /// <param name="filters"></param>
        /// <returns></returns>
        public static bool IsAccurateAuthenticate(ControllerContext Controller, IList<IAuthorizationFilter> filters)
        {
            bool _ISSecure = true;
            foreach (IAuthorizationFilter filer in filters)
            {
                if (filer is AccurateFilterAttribute)//鏈接授權
                {
                    
                    Identity bi = (Identity)Controller.HttpContext.User.Identity;
                    int userid = bi.UserID;

                    HttpRequest httpRequest = new HttpRequest(string.Empty, Controller.HttpContext.Request.Url.ToString(), Controller.HttpContext.Request.QueryString.ToString());
                    bool result = UserGateway.TenantSecurityProvider.IsSignatureUrlsecure(httpRequest,
                                                                                                userid);
                    _ISSecure = result;

                }
            }
            return _ISSecure;
        }
        
        /// <summary>
        /// 判斷用戶是否擁有訪問該URL的權限。
        /// 用戶是否授權訪問該URL包括兩個部分:
        ///1.用戶有該URL對應功能的訪問角色;
        ///2.用戶所在租戶擁有該功能的使用授權。
        /// </summary>
        /// <param name="url">當前訪問的URL</param>
        /// <returns>如果用戶有權訪問該URL,返回true,否則返回False。</returns>
        public static bool IsUserHasPermissionAccess(Type controllerType, string actionName)
        {
           string  url=HttpContextGateWay.Instance.GetHttpContext().Request.Url.AbsolutePath;
           bool result = MembershipProviderGateWay.Instance.IsFuntionUsableByUrl(url);//||MembershipProviderGateWay.Instance.IsFuntionUsableByUrl(fullActionUrl)
           return result;
        }
    }
}


然後就該是過濾器部分了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using BI.Common.Model;
using User.ServiceImp;
using Membership;

namespace Authorization
{
    partial class BIAuthorizationFilter : IAuthorizationFilter
    {
        private Type _controllerType;
        private string _actionName;
        private readonly ControllerContext _controllerContext;
        private readonly IList<IAuthorizationFilter> _filters;
        /// <summary>
        /// 自定義驗證過濾器的構造函數
        /// </summary>
        /// <param name="controllerType">當前的controllerType</param>
        /// <param name="actionName">當前調用的actionName</param>
        public BIAuthorizationFilter(IList<IAuthorizationFilter> filters, Type controllerType, string actionName, ControllerContext controllerContext)
        {
            _controllerType = controllerType;
            _actionName = actionName;
            _controllerContext = controllerContext;
            _filters = filters;
        }
        public void OnAuthorization(AuthorizationContext filterContext)
        {
         //權限驗證部分代碼       
        }
        /// <summary>
        /// 得到完整Url
        /// </summary>
        /// <returns></returns>
        public string GetCurrentFullUrl(AuthorizationContext context)
        {
            return string.Format("{0}://{1}{2}", context.HttpContext.Request.Url.Scheme, context.HttpContext.Request.Url.Host, c            ontext.HttpContext.Request.RawUrl);
        }

        private void RedirectToLogin(AuthorizationContext context, string url)
        {
            context.HttpContext.Response.Redirect(String.Format("{0}?ReturnUrl={1}", FormsAuthentication.LoginUrl, HttpUtility.U            rlEncode(url)));
        }
    }
}

然後就該說說自定義ControllerActionInvoker了。也就是ControllerAction的回調函數:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using BI.Authorization;

namespace BI.Authorization
{
    /// <summary>
    /// 自定義ControllerActionInvoker
    /// </summary>
    public class BIControllerActionInvoker:ControllerActionInvoker
    {
        protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
        {
            //在這裏插入自定義的驗證Filter
            if(filters == null)
            {
                filters = new List<IAuthorizationFilter>();
            }
            IAuthorizationFilter filter = new BIAuthorizationFilter(filters, actionDescriptor.ControllerDescriptor.ControllerType, actionDescriptor.ActionName, controllerContext);
           
            filters.Insert(0,filter);
           
            return base.InvokeAuthorizationFilters(controllerContext, filters, actionDescriptor);
        }        
        
    }
}


最後該說說我們的工廠類了:

自定義的ControllerFactory,可以通過ControllerFactory向Controller中植入相關的ActionInvoker

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace BI.Authorization
{
    /// <summary>
    /// 自定義的ControllerFactory,可以通過ControllerFactory向Controller中植入相關的ActionInvoker
    /// </summary>
    public class BIDefaultControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            requestContext.HttpContext.Response.BufferOutput = true;
            IController instance = base.GetControllerInstance(requestContext, controllerType);
            if (instance != null && typeof(Controller).IsAssignableFrom(controllerType))
            {
                Controller controller = (Controller)instance;
                //依賴注入
                controller.ActionInvoker = new BIControllerActionInvoker();
                //controller.
                return controller;
            }
            else
            {
                return instance;
            }
        }
    }
}

到這裏就把全部的代碼寫在上邊了。
在使用的時候,只需在Global.asax裏邊的Application_Start()裏邊寫入我們在路由裏邊對於我們的方法的註冊就好了:

  protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            ControllerBuilder.Current.SetControllerFactory(new BIDefaultControllerFactory());            
            RegisterRoutes(RouteTable.Routes);
        }

到這個時候,我們在Controller裏邊就不需要打標籤了,默認的就是所有的Action都會進行我們提前設計好的權限驗證了。

 public ActionResult Index()
        {
            ViewData["ReportName"] = "績效考覈進程報表";
            ViewData["EncryptionString"] = str;
            return View();
        }


到這裏就結束了,時間關係,沒有講的很細,不明白的就看代碼吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章