10

ASP.NET MVC でアプリのアクセス許可ベースの承認システムを使用しています。このために、カスタム認証属性を作成しました

public class MyAuthorizationAttribute : AuthorizeAttribute
{
    string Roles {get; set;}
    string Permission {get; set;}
}

ロールまたは特定のアクセス許可キーの両方でユーザーを認証できるように、次のようなアクションの注釈を付けます

public class UserController : Controller
{
    [MyAuthorization(Roles="ADMIN", Permissions="USER_ADD")]
    public ActionResult Add()

    [MyAuthorization(Roles="ADMIN", Permissions="USER_EDIT")]
    public ActionResult Edit()

    [MyAuthorization(Roles="ADMIN", Permissions="USER_DELETE")]
    public ActionResult Delete()
}

次に、 MyAuthorizationAttribute クラスの AuthorizeCore() メソッドを同様のロジック(疑似コード)でオーバーライドします

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    if(user not authenticated)
        return false;

    if(user has any role of Roles)
        return true;

    if(user has any permission of Permissions)
        return true;

    return false;
}

ここまでは正常に動作しています。

ここで、特定のアクションの MyAuthorization 属性承認ロジックに基づいてアクション URL を返すビュー ページでアクション URL を動的に生成できるように、何らかの拡張メソッドが必要です。お気に入り

@Url.MyAuthorizedAction("Add", "User")

ユーザーが管理者ロールを持っているか、「USER_ADD」の権限 (アクションの属性で定義されている) を持っている場合は「User/Add」に URL を返し、それ以外の場合は空の文字列を返します。

しかし、インターネットで数日間検索した後、私はそれを理解できませんでした。:(

これまでのところ、この「セキュリティを意識した」アクションリンクしか見つかりませんでしたか? これは、失敗するまでアクションのすべてのアクション フィルターを実行することによって機能します。

それはいいことですが、MyAuthorizedAction() メソッドを呼び出すたびにすべてのアクション フィルターを実行するのはオーバーヘッドになると思います。また、私のバージョン(MVC 4および.NET 4.5)でも機能しませんでした

必要なのは、認証されたユーザーの役割、アクセス許可 (セッションに保存される) を、承認された役割と特定のアクションのアクセス許可に対してチェックすることだけです。次のようなもの(疑似コード)

MyAuthorizedAction(string actionName, string controllerName)
{
    ActionObject action = SomeUnknownClass.getAction(actionName, controllerName)
    MyAuthorizationAttribute attr = action.returnsAnnationAttributes()

    if(user roles contains any in attr.Roles 
       or 
       user permissions contains any attr.Permissions)
    {
        return url to action
    }
    return empty string
}

アクション属性値を取得する解決策をかなり長い間探していますが、十分なリソースがまったく見つかりませんでした。適切なキーワードを見逃していませんか? :/

誰かが本当に大きな助けになる解決策を私に提供できれば. 解決策を事前にありがとう

4

3 に答える 3

16

アクセス許可に基づいて URL を生成することはおそらくベスト プラクティスではないことに同意しますが、とにかく続行したい場合は、これらを使用してアクションとその属性を見つけることができます。

「アクション」メソッドの取得: 特に領域を使用すると、同じ名前の複数のコントローラ クラスと同じ名前の複数のメソッドを持つことができるため、メソッド情報のコレクションを取得します。これについて心配する必要がある場合は、あいまいさを解消することはあなたに任せます。

public static IEnumerable<MethodInfo> GetActions(string controller, string action)
{
    return Assembly.GetExecutingAssembly().GetTypes()
           .Where(t =>(t.Name == controller && typeof(Controller).IsAssignableFrom(t)))
           .SelectMany(
                type =>
                type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
                    .Where(a => a.Name == action && a.ReturnType == typeof(ActionResult))
             );

}

MyAuthorizationAttributes からアクセス許可を取得します。

public static MyAuthorizations GetMyAuthorizations(IEnumerable<MethodInfo> actions)
{
    var myAuthorization = new MyAuthorizations();
    foreach (var methodInfo in actions)
    {
        var authorizationAttributes =  methodInfo
                .GetCustomAttributes(typeof (MyAuthorizationAttribute), false)
                .Cast<MyAuthorizationAttribute>();
        foreach (var myAuthorizationAttribute in authorizationAttributes)
        {
            myAuthorization.Roles.Add(MyAuthorizationAttribute.Role);
            myAuthorization.Permissions.Add(MyAuthorizationAttribute.Permission);
        }
    }
    return myAuthorization;
}
public class MyAuthorizations
{
    public MyAuthorizations()
    {
        Roles = new List<string>();
        Permissions = new List<string>();
    }
    public List<string> Roles { get; set; }
    public List<string> Permissions { get; set; }
}

最後に、AuthorizedAction 拡張機能: 警告: 特定のコントローラー/アクションのペアに複数の一致がある場合、ユーザーがそれらのいずれかに対して承認されている場合、これは「承認された」URL を提供します...

public static string AuthorizedAction(this UrlHelper url, string controller, string action)
{
    var actions = GetActions(controller, action);
    var authorized = GetMyAuthorizations(actions);
    if(user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
       user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission)))
    {
        return url.Action(controller,action)
    }
    return string.empty;
}

アクセス許可に基づいて URL を生成する際の注意:
これはおそらくベスト プラクティスではありません。状況に応じて、それぞれに独自のレベルの関連性がある場合があります。

  • あいまいさによってセキュリティを達成しようとしているような印象を与えます。URL を表示しないと、そこにあることがわかりません。
  • ページのレンダリングを制御するために他の方法でアクセス許可を既にチェックしている場合 (他の場所のコメントに基づいて行っているように見えます)、URL を明示的に書き出さないことは無意味です。Url.Action メソッドを呼び出さない方がよいでしょう。
  • ユーザーのパーミッションに従ってページのレンダリングをまだ制御していない場合、URL に空の文字列を返すだけでは、壊れた、または壊れているように見えるコンテンツがページに多数残ります。ねえ、このボタンは押しても何もしません!
  • テストとデバッグがより複雑になる可能性があります。パーミッションが正しくないために URL が表示されないのでしょうか、それとも別のバグがあるのでしょうか?
  • AuthorizedAction メソッドの動作に一貫性がないようです。URL を返すこともあれば、空の文字列を返すこともあります。

アクション承認属性によるページ レンダリングの制御:AuthorizedActionメソッドを に 変更しboolean、その結果を使用してページ レンダリングを制御します。

public static bool AuthorizedAction(this HtmlHelper helper, string controller, string action)
{
    var actions = GetActions(controller, action);
    var authorized = GetMyAuthorizations(actions);
    return user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
       user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission))
}

次に、これをかみそりのページで使用します。

@if(Html.AuthorizedAction("User","Add")){
   <div id='add-user-section'>
        If you see this, you have permission to add a user.
        <form id='add-user-form' submit='@Url.Action("User","Add")'>
             etc
        </form>
   </div>
}
else {
  <some other content/>

}
于 2012-11-02T20:57:49.007 に答える
2

Url.Action() で URL を作成するたびにアクション アノテーションをチェックする必要はないと思います。アクションがカスタム認証フィルターで保護されている場合、権限のないユーザーに対しては実行されないため、そのアクションへの URL を非表示にする意味は何ですか? 代わりに、HtmlHelper に拡張メソッドを実装して、現在のユーザーが次のように特定の権限を持っているかどうかを確認できます。

public static bool HasPermission(this HtmlHelper helper, params Permission[] perms)
{
    if (current user session has any permission from perms collection)
    {
        return true;
    }
    else
    {
        return false;
    }
}

次に、ビュー内でヘルパーを使用して、現在のユーザーがアクセスできないボタンとリンクを非表示にすることができます。

@if (Html.HasPermission(Permission.CreateItem))
{
    <a href="@Url.Action("Items", "Create")">Create item</a>
}

もちろん、このように特定のリンクを非表示にするのは UI のためだけです。アクセスの実際の制御は、カスタム認証属性によって行われます。

于 2012-11-02T19:50:30.463 に答える
0

IPrincipal私の唯一の推奨事項は、代わりに次のような拡張メソッドを作成することです

public static bool HasRolesAndPermissions(this IPrincipal instance,
    string roles,
    string permissions,)
{
  if(user not authenticated)
    return false;

  if(user has any role of Roles)
    return true;

  if(user has any permission of Permissions)
    return true;

return false;
}

次に、ビュー/パーシャルのコードは、実際に何をしているのかという点でもう少し読みやすくなります(htmlで何もしていませんが、ユーザーを検証しています)。ビュー/パーシャルのコードは次のようになります

@if (User.HasRolesAndPermissions(roles, permissions)) 
{ 
   @Html.ActionLink(..);
}

各 MVC ページには、現在のユーザーのプロパティWebViewPage.Userがあります。

意図したソリューション(およびセキュリティ対応リンクへのリンク)の問題は、リンクの作成とコントローラーの承認が異なる可能性があることです(私の意見では、このタイプの方法で責任を混合することは悪い習慣です)。新しい承認を拡張IPrincipalすると、次のようになります。

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
  return user.HasRolesAndPermissions(roles, permissions)
}

これで、Authorize Attribute と Views の両方で同じ役割/権限データ ロジックが使用されます。

于 2012-11-02T20:11:38.883 に答える