tl; dr:我的自定义属性覆盖了AuthorizeAttribute的AuthorizeCore方法未正确调用 - 导致无限制访问。
长篇故事:我有一个ASP.NET MVC5应用程序,有四个角色(管理员,调度程序,培训师和学生)。到现在为止我使用了内置的
[Authorize("Administrator")]
对我的控制器(View和API)的类和方法的属性,以针对按预期工作的Active Directory执行Windows身份验证。
现在,我想更改属性中角色的硬编码字符串名称,并从数据库中获取角色名称,以便可以通过数据库配置Active Directory组名称,同时仍然执行身份验证直接针对Active Directory。
工作流:
我发现,Authorize属性需要一个const值,所以我决定实现自定义
[DynamicAuthorize(Roles = Role.AdministratorRole)]
Role.AdministratorRole是一个const字符串。
现在我看到,对于视图控制器,一切都按预期工作。但是当我的API控制器被调用时(t.ex。删除用户),AuthorizeCore(...)方法被而不是被调用。
可能与线程安全或ASP.NET MVC5如何在内部工作有关。我还认为AuthorizeCore(...)方法中的代码并不重要,因为它甚至没有被调用。
我很感激任何提示或建议。
DynamicAuthorize.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
namespace Project.Utilities.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DynamicAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if (SplitString(Users).Length > 0 && !(SplitString(Users).Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
// Role preparation
List<string> allowedRolesRaw = new List<string>(SplitString(Roles));
string allowedRolesAd = "";
// GetRolesActiveDirectoryGroupName(...) loads role names from the database
allowedRolesRaw.ForEach(rc => allowedRolesAd += DomainMapper.GetRolesActiveDirectoryGroupName(DomainMapper.GetRoleIdFromAttributeName(rc), true) + ", ");
if (SplitString(Roles).Length > 0 && !(SplitString(allowedRolesAd).Any(user.IsInRole)))
{
return false;
}
return true;
}
internal static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}
基于:
UsersController.cs (不工作=&gt;未被调用)
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Project.Models;
using Project.Utilities.Attributes;
namespace Project.Controllers.api
{
[DynamicAuthorize(Roles = Role.AdministratorRole)]
public class UsersController : ApiController
{
// Methods in here do not have an attribute
}
}
ManagementController.cs (似乎工作=&gt;始终被调用)
using Project.Utilities.Attributes;
using Hangfire;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Entity;
using System.Net;
using System.Web.Mvc;
using static Project.Utilities.Attributes.AntiForgeryToken;
using Project.Dtos;
using Project.Mapper;
using Project.ViewModels;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
namespace Project.Controllers
{
[DynamicAuthorize(Roles = Role.AdministratorRole + ", " + Role.SchedulerRole)]
public class ManagementController : Controller
{
// Methods here do sometimes have an attribute if a specific action is also allowed for different roles
}
}
答案 0 :(得分:1)
您需要覆盖OnAuthorization
中AuthorizatioAttribute
for System.Web.Http
的{{1}}方法。
public virtual void OnAuthorization(HttpActionContext actionContext);
因此, Web API的过滤器与MVC的过滤器不同。 Web API过滤器位于System.Web.Http.Filters命名空间
中答案 1 :(得分:0)
<强>解决方案:强>
正如所指出的那样,问题是MVC和HTTP控制器实际上使用了不同的实现。因此我的AuthorizeCore(...)
从未被召唤过。解决方案是创建另一个属性(我有DynamicAuthorizeMvc
和DynamicAuthorizeHttp
)从AuthorizeAttribute
命名空间实现System.Web.Http
。我不得不重写方法protected override bool IsAuthorized(HttpActionContext actionContext)
。然后我将DynamicAuthorizeMvc
用于MVC控制器,将DynamicAuthorizeHttp
用于API控制器。工人阶级之后:
<强> DynamicAuthorizeHttp.cs 强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace Project.Utilities.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DynamicAuthorizeHttp : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
IPrincipal user = actionContext.ControllerContext.RequestContext.Principal;
if (user == null || user.Identity == null || !user.Identity.IsAuthenticated)
{
return false;
}
if (SplitString(Users).Length > 0 && !(SplitString(Users).Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
// Role preparation
List<string> allowedRolesRaw = new List<string>(SplitString(Roles));
string allowedRolesAd = "";
allowedRolesRaw.ForEach(rc => allowedRolesAd += DomainMapper.GetRolesActiveDirectoryGroupName(DomainMapper.GetRoleIdFromAttributeName(rc), true) + ", ");
if (SplitString(Roles).Length > 0 && !(SplitString(allowedRolesAd).Any(user.IsInRole)))
{
return false;
}
return true;
}
internal static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}