MVC3应用程序中的只读用户权限,且更改最少

时间:2012-09-18 10:37:43

标签: c# asp.net-mvc-3 architecture filter readonly

我的任务是为ASP.Net MVC3应用程序创建只读用户。即他们可以登录,查看所有数据,但无法更新任何数据。

我已经阅读了很多身份验证文章/框架,例如:Implement secure ASP.NET MVC applicationsFluent Security ConfigurationCreating Action Filters in ASP.Net MVC(以及其他一些,我已经丢失了链接)

大多数方法的问题是它们需要对域/应用程序进行重大更改。我只有一天来实现该功能。

我们有大约一百个控制器,每个控制器平均有4个动作(主要是CRUD操作),并且经历每一个控制器是不可能的。此外,很容易忘记在新代码上添加属性 - 引入错误。

到目前为止,我已经提出了全局过滤器,它拒绝所有基于POST的操作和称为“创建”的控制器操作为只读用户:

public class ReadOnlyFilter : IActionFilter 
{

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var currentUser = HttpContext.Current.User;
        if (currentUser == null || !currentUser.Identity.IsAuthenticated)
            return; // user is not logged in yet


        if (!currentUser.IsInRole("Readonly")) 
            return; // user is not read-only. Nothing to see here, move on!

        // Presume User is read-only from now on.


        // if action is of type post - deny
        if (filterContext.HttpContext.Request.HttpMethod.ToUpper() == "POST")
        {
            filterContext.HttpContext.Response.Redirect("~/ReadOnlyAccess");
        }

        // if action is "Create" - deny access
        if (filterContext.ActionDescriptor.ActionName == "Create")
        {
            filterContext.HttpContext.Response.Redirect("~/ReadOnlyAccess");
        }

        // if action is edit - check if Details action exits -> redirect to it.
        //TODO get this done ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

        return;
    }


    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // blah! have to have this here for IActionFilter
    }
}

接下来我计划为后期操作创建属性[AllowReadOnlyUser],例如更改密码/电子邮件,并在过滤器中允许该操作通过。

我想知道是否有更好的方法来做这种事情?

更新:该应用程序不供公众使用。它在企业界用于跟踪人员,资产和其他无聊的数据。

更新2 :我似乎完成了这项任务。将其作为控制器完成,就像启动一样。完整代码和一些解释can see in my blog

2 个答案:

答案 0 :(得分:4)

您可以将System.Web.Mvc.AuthorizeAttribute用于您的目的。创建一个派生自AuthorizeAttribute的类,并覆盖AuthorizeCore和HandleUnauthorizedRequest方法。在AuthorizeCore中,您确定是否允许用户执行操作,在HandleUnauthorizedRequest中,如果不允许,则确定要显示的内容(例如,显示“NotAllowed”-View)。

创建自定义授权属性后,必须将该属性添加到应受自定义授权保护的所有控制器操作。例如,所有POST方法。但是,如果应该允许所有用户使用POST方法,则只需将该属性添加到该控制器操作中。

答案 1 :(得分:1)

你必须稍微调整一下,在任何人告诉我之前我100%意识到这是一个可怕的黑客。但它也非常有效并且很快实施,这是当时最重要的问题。你当然希望通过一个障碍物来运行它。

还有一些更新面板内容必须被删除,或者更改为jQuery ajax响应结束挂钩或类似的东西,如果需要。

哦,有这个控制只为只读用户运行它:

if (isReadonly && !Page.ClientScript.IsClientScriptBlockRegistered("ReadonlyScriptController"))
{
this.Page.ClientScript.RegisterStartupScript(this.GetType(), 
  "ReadonlyScriptController", "<script>RunReadonlyScript();</script>");
}

脚本:

<script type="text/javascript" src="<%# Page.ResolveUrl("~/ClientScripts/jquery-1.4.2.min.js") %>"></script>
<script type="text/javascript">
    function RunReadonlyScript() {
        //Extend jquery selections to add some new functionality
        //namely, the ability to select elements based on the
        //text of the element.
        $.expr[':'].textEquals = function (a, i, m) {
            var match = $(a).text().match("^" + m[3] + "$");
            return match && match.length > 0;
        };
        //this function does all the readonly work
        var disableStuff = function () {

            //select all controls that accept input, save changes, open a popup, or change form state
            // ** customize this with your own elements **
            $('button, input:not(:hidden), textarea, select,
              a:textEquals("Clear Selection"), 
              a:textEquals("Add Message"), 
              a:textEquals("CCC EVAL"),
              a[id$="availLink"], 
              a[id$="lbtnDelete"], 
              a[id$="lbtnEdit"]')
                //disable input controls
                .attr('disabled', 'disabled')
                //remove onclick javascript
                .removeAttr('onclick')
                //remove all bound click events
                .unbind("click")
                //add a new click event that does nothing
                //this stops <a> links from working
                .click(function (e) {
                    e.preventDefault(); 
                    return false;
                });

            //zap some images with click events that we don't want enabled
            $('img[id$="imgCalendar1"], img[id$="imgDelete"]').hide();
        }
        //if we are loading the home page, don't disable things
        //or the user won't be able to use the search controls
        var urlParts = document.URL.split('/');
        var last2 = urlParts[urlParts.length - 2] + '/' + urlParts[urlParts.length - 1];
        if (last2 !== 'home/view') {
            //disable everything initially
            disableStuff();
            //setup a timer to re-disable everything once per second
            //some tab panels need this to stay disabled
            setInterval(disableStuff, 1000);
            //some pages re-enable controls after update panels complete
            //make sure to keep them disabled!
            Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(disableStuff);
            Sys.WebForms.PageRequestManager.getInstance().add_endRequest(disableStuff);
        }
    }
</script>