bearer - ¿Cómo puedo implementar una Autorización basada en Reclamos con ASP.NET WebAPI sin utilizar Roles?
oauth authentication asp net web api (1)
Tengo una aplicación ASP.Net WebAPI 2 que usa Reclamaciones. Los reclamos se almacenan como dos columnas adicionales en una tabla estándar 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)
);
He modificado la clase ApplicationUser de esta manera:
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; }
}
En mi método de registro, agrego nuevos datos para SubjectId:
var user = new ApplicationUser() {
UserName = model.UserName,
SubjectId = 25,
LocationId = 4
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
¿Alguien puede ayudarme a decir cómo puedo ahora restringir el acceso a un controlador basado en este SubjectId en el nivel de controlador y también en el nivel de método con algo similar a esto?
[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);
}
Durante meses he estado buscando un ejemplo, pero aparte de alguna referencia al producto ThinkTexture y al siguiente enlace, no he encontrado nada
Actualizar:
#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);
}
}
Puede lograr eso si anula el atributo Authorize
. En tu caso, debería ser algo como esto:
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);
}
}
En su controlador al que desea restringir el acceso, use el atributo ClaimsAuthorize
lugar del Authorize
normal:
[ClaimsAuthorize(
SubjectID = "1,2",
LocationID = "5,6,7")]
[RoutePrefix("api/Content")]
public class ContentController : BaseController
{
....
}