首页 > 解决方案 > 自定义授权属性角色不起作用 web api 身份验证

问题描述

我在使用 web api azure 广告身份验证时遇到问题

我有一个像下面这样的控制器,它给出了正确的响应,但是一个具有自定义授权角色的控制器抛出错误为“身份验证已用于此请求”。

[RoutePrefix("api/hospitals")]
public class hospitals : ApiController
{
    [Route("GetAll")]
    [HttpGet]
    [Authorize]
    public async Task<IEnumerable<Hospitals>> GetAll()
    {
        // return ok;
    }
    [Route("Getbeds")]
    [HttpGet]
    [SmAuthorize(Constants.Roles.Admin,
        Constants.Roles.HotSpitalAdmin,
        Constants.Roles.QA)]
    public async Task<IEnumerable<Hospitals>> Getbeds()
    {
        // return ok;
    }
}

Getbeds 方法因“已请求授权”而引发错误。

请也找到我自定义属性类

public class SmAuthorizeAttribute : AuthorizeAttribute
{
    public SmAuthorizeAttribute(params string[] roles)
    {
        this.Roles = string.Join(",", roles.Select(s => s.Trim()).ToArray());
    }
}

有人可以帮忙吗?

标签: c#azure-active-directoryauthorize-attributeapi-authorization

解决方案


您可以参考Derek Greer 对Dot Net core的这个SO 问题的回答,另外我将在下面重申答案 -

ASP.Net Core 团队推荐的方法是使用此处完整记录的新策略设计。新方法背后的基本思想是使用新的 [Authorize] 属性来指定“策略”(例如 [Authorize(Policy = "YouNeedToBe18ToDoThis")],其中策略在应用程序的 Startup.cs 中注册以执行某些块代码(即确保用户有年龄声明,其中年龄为 18 岁或以上)。

策略设计是对框架的一个很好的补充,应该赞扬 ASP.Net 安全核心团队的引入。也就是说,它并不适合所有情况。这种方法的缺点是它无法为简单地断言给定控制器或动作需要给定声明类型的最常见需求提供方便的解决方案。在应用程序可能具有数百个离散权限管理单个 REST 资源(“CanCreateOrder”、“CanReadOrder”、“CanUpdateOrder”、“CanDeleteOrder”等)上的离散权限的情况下,新方法要么需要重复的一对一策略名称和声明名称之间的一个映射(例如 options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));),或编写一些代码在运行时执行这些注册(例如,从数据库中读取所有声明类型并在循环中执行上述调用)。在大多数情况下,这种方法的问题在于它是不必要的开销。

虽然 ASP.Net Core 安全团队建议永远不要创建自己的解决方案,但在某些情况下,这可能是开始时最谨慎的选择。

下面是一个实现,它使用 IAuthorizationFilter 提供一种简单的方法来表达给定控制器或操作的声明要求:

public class ClaimRequirementAttribute : TypeFilterAttribute
{
    public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
    {
        Arguments = new object[] {new Claim(claimType, claimValue) };
    }
}

public class ClaimRequirementFilter : IAuthorizationFilter
{
    readonly Claim _claim;

    public ClaimRequirementFilter(Claim claim)
    {
        _claim = claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
        if (!hasClaim)
        {
            context.Result = new ForbidResult();
        }
    }
}


[Route("api/resource")]
public class MyController : Controller
{
    [ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
    [HttpGet]
    public IActionResult GetResource()
    {
        return Ok();
    }
}

.NET Framework的部分答案-

推荐的自定义属性类:

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

推荐阅读