在现代Web应用中,权限控制是一个核心功能,它确保了用户只能访问他们被授权的资源。本文将介绍一种基于角色的权限控制实现方法,该方法使用.NET MVC和JavaScript技术,为服务器端和客户端提供了一种通用的解决方案。
本系统是一个Web应用,它能够对用户进行认证和授权。用户登录后,包括分配给他们的功能在内的用户详细信息被存储为会话对象。在请求页面时,服务器端和客户端将根据用户的角色和权限来控制访问。
        用户登录时,系统会将用户信息和分配给他们的功能存储在会话对象中。当用户请求页面时,服务器端的PermissionFilter会验证请求;如果用户未认证,则会被重定向到登录页面。同时,PermissionFilter还会根据用户角色分配的功能/权限来授权请求;如果用户未授权,则会渲染一个NotAuthorized页面。
    
        在客户端,PermissionChecker脚本会获取用户被授权的功能的elementControlID,并从DOM中移除其余元素。
    
        在数据库中,每个功能都有一个唯一的controlID。在客户端,每个功能都被分配了一个唯一的controlID,使用属性data-feature="。在服务器端,通过与请求中的控制器名称和操作名称关联的功能来检查访问权限。
    
本系统使用Visual Studio 2012和.NET MVC技术构建。
        每个用户都有一个角色,每个角色被分配了一个或多个功能/操作(例如:添加/编辑/删除等)。每个功能/权限都是一个由控制器名称和操作名称识别的操作,具有唯一的controlID(Name),以便在网页上识别。
    
        在服务器端,有一个动作过滤器,它根据分配给登录用户角色的功能/权限来授权调用的动作。它还验证认证;访问AfterLogin页面。对于任何未授权的活动,它返回一个NotAuthorizedToUseThisContent页面。
    
在客户端,一个脚本会在页面加载和任何Ajax请求完成后,从DOM中移除未分配给登录用户角色的功能。
PermissionFilter.cs是一个动作过滤器,它在控制器中的每个请求上运行。它使用分配给登录用户角色的功能/权限的动作和控制器名称来认证和授权(loggedin)。
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            HttpRequestBase request = filterContext.HttpContext.Request;
            HttpResponseBase response = filterContext.HttpContext.Response;
            if ((request.AcceptTypes.Contains("application/json")) == false)
            {
                if (request.IsAjaxRequest())
                {
                    // Preventing caching of ajax request in IE browser
                    response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
                    response.Cache.SetValidUntilExpires(false);
                    response.Cache.SetCacheability(HttpCacheability.NoCache);
                    response.Cache.SetNoStore();
                }
                string currentActionName = filterContext.ActionDescriptor.ActionName;
                string currentControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
                // get LoggedInUser Permissions
                PermissionManager userPermissions = PermissionManager.getPermissions();
                // All Features are allowed for SuperAdmin - disablePermissioning
                if (userPermissions != null)
                {
                    // For logout page
                    filterContext.Controller.ViewBag.LoggedInAdministrator = PermissionManager.GetLoggedInUser();
                    if (PermissionManager.enablePermissioningSystem == true)
                    {
                        // Not all actions are feature in the application
                        bool isCurrentActionAFeature = Service.isFeaturePresentInList(userPermissions.allFeatures, currentControllerName, currentActionName);
                        if (isCurrentActionAFeature)
                        {
                            bool hasPermission = false;
                            hasPermission = Service.isFeaturePresentInList(userPermissions.accessibleFeatures, currentControllerName, currentActionName);
                            if (!hasPermission)
                            {
                                // return 'not authorized' content
                                filterContext.Result = new ViewResult
                                {
                                    ViewName = "~/Views/shared/unauthorizedactivity.cshtml"
                                };
                            }
                        }
                    }
                }
                // Redirect to login Page
                else
                {
                    filterContext.Controller.TempData["Message"] = "Please login to continue.";
                    filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary 
                                { 
                                    { "controller", "home" }, 
                                    { "action", "login" }, 
                                    { "Area", "" } 
                                });
                }
            }
        }
    
        服务器端的动作被装饰有PermissionFilter属性,用于过滤对该动作的请求。
    
这个脚本在页面加载和任何Ajax请求时,从DOM中移除未授权的功能元素。
        var allowedFeatureList = new Array();
        var enablePermission = false;
        /*
        Register ajax complete callback to apply 'Userpermissions' on ajax requests
        */
        /*
        Ajax OnComplete START*/
        //
        For - Authorizing after ajax response
        $(document).ajaxComplete(function(event, request, settings) {
            var allDataFeatureElements = $('[data-feature]');
            if (enablePermission == true) {
                $(allDataFeatureElements).each(function() {
                    var currentfid = $(this).data("feature");
                    // Check If the current data-feature element in Page lies in allowed Features list
                    var result = $.inArray(currentfid, allowedFeatureList);
                    if (result != -1) {
                    }
                    else {
                        // Remove element from DOM if not authorized to access it
                        $(this).addClass("noDisplay");
                        $(this).remove();
                    }
                });
            }
        });
        /*
        Ajax OnComplete END*/
        //
        For - Authorizing after page load
        $(function() {
            // To disable caching of ajax requests in IE browsers
            $.ajaxSetup({ cache: false });
            var allDataFeatureElements = $('[data-feature]');
            // Get allowed Features List for login user
            $.getJSON("/home/getAllAccessibleFeatureControlIDJSON", function(data) {
                enablePermission = data.enablePerm;
                if (data.enablePerm == true) {
                    for (var i = 0; i < data.controlIDS.length; i++) {
                        allowedFeatureList.push(data.controlIDS[i]);
                    }
                    $(allDataFeatureElements).each(function() {
                        var currentfid = $(this).data("feature");
                        var result = $.inArray(currentfid, allowedFeatureList);
                        if (result != -1) {
                        }
                        else {
                            $(this).addClass("noDisplay");
                            $(this).remove();
                        }
                    });
                }
            });
        });
    
页面加载和ajaxComplete函数用于DOM过滤。功能仅在页面加载时从服务器获取,并在ajaxComplete中使用,因为如果在ajaxComplete中获取,会再次触发它,最终导致无限循环。
        在用户列表页面中,登录用户仅被分配了EditUser权限,其他功能通过脚本从页面中移除。