12

GetResource(int resourceId) アクションを持つ .net Web API があるとします。このアクション (指定された ID を持つ) は、その ID に関連付けられたユーザーに対してのみ許可される必要があります (リソースは、たとえば、ユーザーによって書かれたブログ投稿である可能性があります)。

これは多くの方法で解決できますが、例を以下に示します。

    public Resource GetResource(int id)
    {
        string name = Thread.CurrentPrincipal.Identity.Name;
        var user = userRepository.SingleOrDefault(x => x.UserName == name);
        var resource = resourceRepository.Find(id);

        if (resource.UserId != user.UserId)
        {
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        return resource;
    }

ユーザーが何らかのメカニズムによって認証されている場合。

ここで、たとえば管理者タイプのユーザーにも、(同じ ID で) エンドポイントを使用する権限を与えたいとしましょう。このユーザーは、リソースとの直接的な関係はありませんが、そのタイプ (またはロール) のために承認を持っています。これは、ユーザーが管理者タイプであるかどうかを確認し、リソースを返すだけで解決できます。

すべてのアクションで認証コードを記述する必要がないように、これを一元化する方法はありますか?

編集 回答に基づいて、質問を明確にする必要があると思います。

私が本当に求めているのは、リソースベースの承認を可能にするメカニズムですが、同時に一部のユーザーが同じエンドポイントと同じリソースを消費できるようにするメカニズムです。以下のアクションは、この特定のエンドポイントとこの特定の役割 (管理者) についてこれを解決します。

    public Resource GetResource(int id)
    {
        string name = Thread.CurrentPrincipal.Identity.Name;
        var user = userRepository.SingleOrDefault(x => x.UserName == name);
        var resource = resourceRepository.Find(id);

        if (!user.Roles.Any(x => x.RoleName == "Admin" || resource.UserId != user.UserId)
        {
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        return resource;
    }

私が求めているのは、この問題を解決する一般的な方法であり、同じ目的で 2 つの異なるエンドポイントを作成したり、すべてのエンドポイントでリソース固有のコードを作成したりする必要がありません。

4

5 に答える 5

4

リソース ベースの承認については、クレーム ベースの IDを使用し、ユーザー ID をクレームとして埋め込むことをお勧めします。ID からクレームを読み取る拡張メソッドを記述します。したがって、サンプル コードは次のようになります。

public Resource GetResource(int id)
{
     var resource = resourceRepository.Find(id);
    if (resource.UserId != User.Identity.GetUserId())
    {
        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    return resource;
}

コードをさらに単純化したい場合は、ユーザー データとリソース リポジトリを認識してコードを集中化する UserRepository を作成できます。コードは次のようになります。

public Resource GetResource(int id)
{
    return User.Identity.GetUserRepository().FindResource(id);
}

役割ベースの承認の場合、AuthorizeAttributeはそれを処理するのに最適な場所であり、別のアクションまたはコントローラーを使用することをお勧めします。

[Authorize(Roles = "admin")]
public Resource GetResourceByAdmin(int id)
{
    return resourceRepository.Find(id);
}

[編集] OP が単一のアクションを使用してさまざまなタイプのユーザーを処理する場合、個人的にはユーザー リポジトリ ファクトリを使用することを好みます。アクションコードは次のとおりです。

public Resource GetResource(int id)
{
    return User.GetUserRepository().FindResource(id);
}

拡張方法は次のとおりです。

public static IUserRepository GetUserRepository(this IPrincipal principal)
{
    var resourceRepository = new ResourceRepository();
    bool isAdmin = principal.IsInRole("Admin");
    if (isAdmin)
    {
        return new AdminRespository(resourceRepository);
    }
    else
    {
       return new UserRepository(principal.Identity, resourceRepository);
    }
}

AuthorizeAttribute を使用してリソースごとの認証を行いたくない理由は、リソースごとに所有権を確認するためのコードが異なる可能性があるためです。コードを 1 つの属性に集中させるのは難しく、実際には不要な追加の DB 操作が必要です。もう 1 つの問題は、AuthroizeAttribute がパラメーター バインドの前に発生することです。そのため、アクションのパラメーターがルート データから取得されていることを確認する必要があります。そうしないと、たとえば投稿本文から、パラメーター値を取得できません。

于 2013-09-17T16:49:23.413 に答える
2

承認を外部化する必要があります。承認ロジック全体を別のレイヤーまたはサービスに移動したい。

それを可能にするいくつかのフレームワークが (異なる言語で) あります。.NET の世界では、他の回答で示唆されているように、クレームベースの承認があります。Microsoft には、それに関する優れた記事があります

標準化されたアプローチ、つまり XACML (eXtensible Access Control Markup Language) を採用することをお勧めします。XACML は次の 3 つのことを提供します。

  • はい/いいえの決定を行うことができるポリシー決定ポイント (PDP - それはあなたの承認サービスです) の概念を備えた標準アーキテクチャ
  • ユーザー属性やリソース情報を含む任意の数のパラメーター/属性を使用して、承認ロジックを表現する標準言語。
  • 承認の質問を PDP に送信するための要求/応答スキーム。

あなたの例をもう一度見ると、次のようなものがあります。

public Resource GetResource(int id)
{
     var resource = resourceRepository.Find(id);
    if (isAuthorized(User.Identity,resource))
    {
        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    return resource;
}

public bool isAuthorized(User u, Resource r){
   // Create XACML request here
   // Call out to PDP
   // return boolean decision
}

PDP には次のルールが含まれます。

  • resource.owner==user.id の場合に限り、ユーザーはリソースに対して action==view を実行できます
  • role==administrator を持つユーザーは、リソースに対して action==view を実行できます。

XACML の利点は、コードとは独立して承認規則/ロジックを拡張できることです。これは、ロジックが変更されるたびにアプリケーション コードを変更する必要がないことを意味します。XACML は、デバイス ID、IP、時刻など、より多くのパラメーター/属性にも対応できます。最後に、XACML は .NET に固有のものではありません。多くの異なるフレームワークで機能します。

XACMLについては、こちらと、承認について書いている私のブログをご覧ください。ウィキペディアにも、このトピックに関する適切なページがあります。

于 2013-09-19T18:39:47.280 に答える
2

この特定の承認規則を必要とするアクションに適用できるカスタムSystem.Web.Http.AuthorizeAttributeの実装を検討します。カスタム認証では、ユーザーが Admins グループのメンバーである場合、またはリソースの作成者である場合にアクセスを許可できます。

編集:

OPの編集に基づいて、私が言ったことを拡張させてください。AuthorizeAttribute をオーバーライドすると、次のようなロジックを追加できます。

public class AuthorizeAdminsAndAuthors : System.Web.Http.AuthorizeAttribute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        return currentUser.IsInRole("Admins") || IsCurrentUserAuthorOfPost(actionContext);
    }

    private bool IsCurrentUserAuthorOfPost(HttpActionContext actionContext)
    {
        // Get id for resource from actionContext
        // look up if user is author of this post
        return true;
    }

これは疑似コードですが、アイデアを伝える必要があります。要件に基づいて承認を決定する単一の AuthorizeAttribute がある場合: 現在の要求が投稿の作成者または管理者からのものである場合、このレベルの承認が必要な任意のリソースに AuthorizeAdminsAndAuthors 属性を適用できます。したがって、リソースは次のようになります。

[AuthorizeAdminsAndAuthors]
public Resource GetResource(int id)
{
    var resource = resourceRepository.Find(id);
    return resource;
}
于 2013-09-17T17:07:51.993 に答える
1

また、.NET 4.5 以降に含まれているクレーム ベースの承認アプローチも参照してください。

http://leastprivilege.com/2012/10/26/using-claims-based-authorization-in-mvc-and-web-api/

于 2013-09-18T06:30:04.743 に答える