Asp.net

如何在不使用角色的情況下使用 ASP.NET WebAPI 實現基於聲明的授權?

  • November 7, 2014

我有一個使用聲明的 ASP.Net WebAPI 2 應用程序。聲明儲存為標準 Identity2 AspNetUsers 表中的兩個附加列:

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);
   }
}

如果您覆蓋該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
{
    ....
}

引用自:https://stackoverflow.com/questions/26806063