5

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

我已经阅读了很多身份验证文章/框架,例如:实现安全的 ASP.NET MVC 应用程序,或流畅的安全配置,或在 ASP.Net MVC 中创建操作过滤器(以及其他一些,我已经失去了链接) .

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

我们有大约 100 个控制器,每个控制器平均有 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:我似乎已经完成了这项任务。开始时作为控制器完成。您可以在我的博客中看到完整的代码和一些解释。

4

2 回答 2

4

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

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

于 2012-09-18T11:56:15.760 回答
1

你必须稍微调整一下,在有人告诉我之前,我 100% 意识到这是一个可怕的 hack。但它也非常有效并且实施得非常迅速,这是当时最重要的问题。当然,您需要通过一个 obsficator 来运行它。

如果需要,那里还有一些更新面板的东西必须被删除,或者更改为 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>
于 2012-09-18T12:44:20.363 回答