web-dev-qa-db-ja.com

Web APIでのカスタムAuthorizeAttributeの作成(.Net Framework)

WebAPIでOAuth2.0 Owin(パスワード付与)を使用しています。初期トークンレスポンスは次のようになります。

{
    "access_token": "_ramSlQYasdsRTWEWew.....................",
    "token_type": "bearer",
    "expires_in": 17999,
    "permissions": {
        "user": [
            "Add",
            "Update",
            "Delete"
        ],
        "Product": [
            "Read",
            "Create"
        ]
    }
}

カスタマイズ 対応するユーザーの特権を保持するpermissionsという新しいキーを作成することで応答しました。

ここから、Authorize Attributeを使用してAPIを呼び出すための十分な権限がユーザーにあるかどうかを確認することにより、Resource serverからの各リクエストを検証する必要があります。

here から同様の例を見つけましたが、ここではDot net Coreを扱っていますが、これは私の場合には適していません。

難しい部分は、permission JSONキー自体がArrayListと複雑になっていることです

[CustomAuthorize(PermissionItem.Product, PermissionAction.Read)]
    public async Task<IActionResult> Index()
    {
        return View(Index);
    }

public class CustomAuthorize : AuthorizeAttribute {
    public AuthorizeAttribute (PermissionItem item, PermissionAction action) {
        //Need to initalize the Permission Enums
    }
    public override void OnAuthorization (HttpActionContext actionContext) {
        //Code to get the value from Permissions ArrayList and compare it with the Enum values
    }
}

上記は私が持っているアイデアです。しかし、PermissionsキーとEnumの比較が複雑であるため、先に進むことができませんでした。

また、ユーザーのアクセス許可が更新と同様に追加である場合、コントローラーの前に2つの属性条件を作成する必要があることを意味するような質問があります。

いいね

[CustomAuthorize(PermissionItem.User, PermissionAction.Add)]
[CustomAuthorize(PermissionItem.User, PermissionAction.Update)]

これにより、属性の行が追加されます。それで、|で区切られた単一の条件のようにする方法はありますか?

[CustomAuthorize(PermissionItem.User, PermissionAction.Update|PermissionAction.Add)]
10
Jayendran

CustomAuthorizeコンストラクターに複数のPermissionアクションを許可しないのはなぜですか。

_public class CustomAuthorize : AuthorizeAttribute
{
    private readonly PermissionAction[] permissionActions;

    public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
    {
        this.permissionActions = permissionActions;
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
        if (!currentIdentity.IsAuthenticated) {
            // redirect to access denied page
        }

        var userName = currentIdentity.Name;
        // step 1 : retrieve user object

        // step 2 : retrieve user permissions

        // step 3 : match user permission(s) agains class/method's required premissions

        // step 4 : continue/redirect to access denied page
    }
}
_

そして、クラスに注釈を付けます:[CustomAuthorize(PermissionItem.User, PermissionAction.Update, PermissionAction.Add)]

ここでは、OPが何を達成したいのかわかりません。 HTTPリクエストに依存してアクセス権を提供している場合、それは大きなセキュリティホールです。各リクエストで、データベースからユーザーのアクセス権情報を取得する必要があり、クラス/メソッドの必要な許可と一致します。

経験則として、現在のユーザーが持っている権限を要求オブジェクトに依存してはいけません。データストアから取得する必要があります。

CustomAttributeの実装

_public class CustomAuthorize : System.Web.Http.AuthorizeAttribute
{
    private readonly PermissionAction[] permissionActions;

    public CustomAuthorize(PermissionItem item, params PermissionAction[] permissionActions)
    {
        this.permissionActions = permissionActions;
    }

    protected override Boolean IsAuthorized(HttpActionContext actionContext)
    {
        var currentIdentity = actionContext.RequestContext.Principal.Identity;
        if (!currentIdentity.IsAuthenticated)
            return false;

        var userName = currentIdentity.Name;
        using (var context = new DataContext())
        {
            var userStore = new UserStore<AppUser>(context);
            var userManager = new UserManager<AppUser>(userStore);
            var user = userManager.FindByName(userName);

            if (user == null)
                return false;

            foreach (var role in permissionActions)
                if (!userManager.IsInRole(user.Id, Convert.ToString(role)))
                    return false;

            return true;
        }
    }
}
_
2
Dipen Shah

フラグとバイナリ演算を使用して、次のことができます。異なる操作を一緒に。

次のコードは、実行方法の簡単な例を示しています

class Program {
    static void Main(string[] args) {
        Test test = new Test();
        CustomAuthorizeAttribute customAuthorizeAttribute = (CustomAuthorizeAttribute)Attribute.GetCustomAttribute(typeof(Test), typeof(CustomAuthorizeAttribute));

        customAuthorizeAttribute.Test();

        Console.ReadKey();
    }
}

[CustomAuthorize(PermissionActions = PermissionAction.Add | PermissionAction.Delete)]
public class Test {

}

public class CustomAuthorizeAttribute : Attribute {
    public PermissionAction PermissionActions { get; set; }

    public void Test() {
        if ((PermissionActions & PermissionAction.Add) == PermissionAction.Add) Console.WriteLine("Add");
        if ((PermissionActions & PermissionAction.Delete) == PermissionAction.Delete) Console.WriteLine("Delete");
        if ((PermissionActions & PermissionAction.Update) == PermissionAction.Update) Console.WriteLine("Update");
    }
}

public enum PermissionAction {
    Add = 1,
    Update = 2,
    Delete = 4
}

次の出力が得られます

enter image description here

1
3dd

認証用のAPIフィルターを以下に作成しました。

ここでは、「SecretToken」、「MerchantKey」、この2つのキーがAPIリクエストで渡されます。 「IsValidMerchant」関数を使用してデータベースからこの2つを検証しています。

IsValidMerchantこの関数は、それぞれの値が格納されているデータベーステーブルと直接接続します

public void OnAuthorization(AuthorizationFilterContext actionContext)
{
  const string secretTokenName = "SecretToken";
  const string merchentKeyName = "MerchantKey";
  bool isValid = false;

  if (!actionContext.Filters.Any(item => item is IAllowAnonymousFilter))
    {
     CPServiceResponse response = new CPServiceResponse();
     var secretToken = actionContext.HttpContext.Request.Headers[secretTokenName].FirstOrDefault();
     var merchentKey = actionContext.HttpContext.Request.Headers[merchentKeyName].FirstOrDefault();

      isValid = this.IsValidMerchant(merchentKey, secretToken,_productCode);

       if (isValid == false)
        {
          response.Status = (int)HttpStatusCode.Unauthorized;
          response.Message = Hegic.Shared.Resource.Common.UnauthorizedRequestError;
          actionContext.Result = new JsonResult("")
                {
                    Value = new { Status = response }
                };
         }
      }
  }
1
Parth Akbari