5

ASP.Net MVC3 アプリケーションの読み取り専用ユーザーを作成するタスクがあります。つまり、ログインしてすべてのデータを表示できますが、データを更新することはできません。

次のような多くの認証記事/フレームワークを読みました:セキュアな ASP.NET MVC アプリケーションの実装、またはFluent Security Configuration、またはASP.Net MVC でのアクション フィルターの作成(およびその他のいくつかについては、既にリンクを失っています) .

ドメイン/アプリケーションへの大幅な変更を必要とするほとんどのアプローチの問題。そして、この機能を実装するのに1 日しかありません。

コントローラーごとに平均 4 つのアクション (主に CRUD 操作) を持つ約 100 のコントローラーがあり、それらのすべてを実行することは問題外です。また、新しいコードに属性を付けることを忘れがちです - バグが発生します。

これまでのところ、すべての 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

you can use the System.Web.Mvc.AuthorizeAttribute for your purpose. Create a class that derives from AuthorizeAttribute and override the methods AuthorizeCore and HandleUnauthorizedRequest. In AuthorizeCore you determine if a user is allowed to execute an action, in HandleUnauthorizedRequest you determine what to display if he isn't allowed (for instance, show a "NotAllowed"-View).

After creating your custom authorization attribute, you have to add the attribute to all controller actions that should be protected by your custom authorization. For instance, all POST-methods. But if there is a POST-method that should be allowed for all users, you just don't add the attribute to that controller action.

于 2012-09-18T11:56:15.760 に答える
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>
于 2012-09-18T12:44:20.363 に答える