web-dev-qa-db-ja.com

ロールを使用せずにASP.NET Web APIでクレームベースの承認を実装するにはどうすればよいですか?

クレームを使用するASP.Net WebAPI 2アプリケーションがあります。クレームは、標準のIdentity2 AspNetUsersテーブルに2つの追加の列として格納されます。

CREATE TABLE [dbo].[AspNetUsers] (
    [Id]                   INT            IDENTITY (1, 1) NOT NULL,
    ....
    [SubjectId]            INT            DEFAULT ((0)) NOT NULL,
    [LocationId]           INT            DEFAULT ((0)) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
);

ApplicationUserクラスを次のように変更しました。

public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            ClaimsIdentity userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
            // Add custom user claims here
            userIdentity.AddClaim(new Claim("SubjectId", this.SubjectId.ToString()));
            userIdentity.AddClaim(new Claim("LocationId", this.LocationId.ToString()));
            return userIdentity;
        }

        public int SubjectId { get; set; }
        public int LocationId { get; set; }

    }

私の登録メソッドで、SubjectIdの新しいデータを追加します。

    var user = new ApplicationUser() { 
        UserName = model.UserName, 
        SubjectId = 25,
        LocationId = 4
    };

    IdentityResult result = await UserManager.CreateAsync(user, model.Password);

コントローラーレベルで、およびメソッドレベルでも、このSubjectIdに基づいてコントローラーへのアクセスを制限する方法を教えてもらえますか?

[Authorize(SubjectId = "1,25,26")]
[RoutePrefix("api/Content")]
public class ContentController : BaseController
{

    [Authorize(LocationId = "4")]
    [Route("Get")]
    public IQueryable<Content> Get()
    {
        return db.Contents;
    }

    [Authorize(SubjectId = "25")]
    [Route("Get/{id:int}")]
    public async Task<IHttpActionResult> Get(int id)
    {
        Content content = await db.Contents.FindAsync(id);
        if (content == null)
        {
            return NotFound();
        }
        return Ok(content);
    }

数か月前から例を探していましたが、ThinkTexture製品への参照と次のリンク以外は何も見つかりませんでした

更新:

#region Assembly System.Web.Http.dll, v5.2.2.0
// C:\Users\Richard\GitHub\abilitest-server\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll
#endregion

using System;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace System.Web.Http
{
    // Summary:
    //     Specifies the authorization filter that verifies the request's System.Security.Principal.IPrincipal.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeAttribute : AuthorizationFilterAttribute
    {
        // Summary:
        //     Initializes a new instance of the System.Web.Http.AuthorizeAttribute class.
        public AuthorizeAttribute();

        // Summary:
        //     Gets or sets the authorized roles.
        //
        // Returns:
        //     The roles string.
        public string Roles { get; set; }
        //
        // Summary:
        //     Gets a unique identifier for this attribute.
        //
        // Returns:
        //     A unique identifier for this attribute.
        public override object TypeId { get; }
        //
        // Summary:
        //     Gets or sets the authorized users.
        //
        // Returns:
        //     The users string.
        public string Users { get; set; }

        // Summary:
        //     Processes requests that fail authorization.
        //
        // Parameters:
        //   actionContext:
        //     The context.
        protected virtual void HandleUnauthorizedRequest(HttpActionContext actionContext);
        //
        // Summary:
        //     Indicates whether the specified control is authorized.
        //
        // Parameters:
        //   actionContext:
        //     The context.
        //
        // Returns:
        //     true if the control is authorized; otherwise, false.
        protected virtual bool IsAuthorized(HttpActionContext actionContext);
        //
        // Summary:
        //     Calls when an action is being authorized.
        //
        // Parameters:
        //   actionContext:
        //     The context.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     The context parameter is null.
        public override void OnAuthorization(HttpActionContext actionContext);
    }
}
28
user3568783

Authorize属性をオーバーライドすると、これを実現できます。あなたの場合、それはこのようなものでなければなりません:

public class ClaimsAuthorize : AuthorizeAttribute
{
    public string SubjectID { get; set; }
    public string LocationID { get; set; }

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        ClaimsIdentity claimsIdentity;
        var httpContext = HttpContext.Current;
        if (!(httpContext.User.Identity is ClaimsIdentity))
        {
            return false;
        }      

        claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
        var subIdClaims = claimsIdentity.FindFirst("SubjectId");
        var locIdClaims = claimsIdentity.FindFirst("LocationId");
        if (subIdClaims == null || locIdClaims == null)
        {
            // just extra defense
            return false;
        }

        var userSubId = subIdClaims.Value;
        var userLocId = subIdClaims.Value;

        // use your desired logic on 'userSubId' and `userLocId', maybe Contains if I get your example right?
        if (!this.SubjectID.Contains(userSubId) || !this.LocationID.Contains(userLocId))
        {
            return false;
        }

        //Continue with the regular Authorize check
        return base.IsAuthorized(actionContext);
    } 
}

アクセスを制限したいコントローラーで、通常のClaimsAuthorize属性の代わりにAuthorize属性を使用します。

[ClaimsAuthorize(
    SubjectID = "1,2",
    LocationID = "5,6,7")]
[RoutePrefix("api/Content")]
public class ContentController : BaseController
{
     ....
}
37
Omri Aharon