我有一个非常简单的场景。我想用自定义授权属性装饰我的控制器/操作。如果任何属性有效,则应授予授权。例如,
[MyAuth(1)]
[MyAuth(2)]
public class MyController : Controller
{
...
}
我无法将参数组合到单个授权属性中。以上示例仅为简化示例。
如果任一属性授权用户,我希望用户获得授权。我假设ActionFilterAttribute
或AuthorizeAttribute
有办法看到其他过滤器已被执行并等待执行,但没有这样的运气。
我怎样才能做到这一点?由于属性似乎没有任何意识,可能是HttpModule
?自定义ControllerActionInvoker
?
答案 0 :(得分:6)
昨晚我设法让这个工作了。我的解决方案如下。该属性非常标准,我已经修剪了实际的授权部分。有趣的事情发生在HasAssignedAcccessActionInvoker
。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class RequiresAssignedAccess : AuthorizeAttribute
{
public int AccessType { get; private set; }
public int IdType { get; private set; }
public int IdValue { get; private set; }
public int Level { get; private set; }
public RequiresAssignedAccess(int accessType, int idType, int idValue, int level)
{
...
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
bool retval = ...
return retval;
}
}
HasAssignedAcccessActionInvoker
继承自标准动作调用者,但我覆盖了InvokeAuthorizationFilters
方法以添加我们需要的授权逻辑。标准调用者只是通过授权过滤器旋转,如果它们中的任何一个返回结果,它就会打破循环。
public class HasAssignedAcccessActionInvoker : ControllerActionInvoker
{
protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authCtx = new AuthorizationContext(controllerContext, actionDescriptor);
/*
* If any of the filters are RequiresAssignedAccess, default this to false. One of them must authorize the user.
*/
bool hasAccess = !filters.Any(f => f is RequiresAssignedAccess);
foreach (IAuthorizationFilter current in filters)
{
/*
* This sets authorizationContext.Result, usually to an instance of HttpUnauthorizedResult
*/
current.OnAuthorization(authCtx);
if (current is RequiresAssignedAccess)
{
if (authCtx.Result == null)
{
hasAccess = true;
}
else if (authCtx.Result is HttpUnauthorizedResult)
{
authCtx.Result = null;
}
continue;
}
if (authCtx.Result != null)
break;
}
if (!hasAccess && authCtx.Result == null)
authCtx.Result = new HttpUnauthorizedResult();
return authCtx;
}
}
我不得不看看MVC与ILSpy的内部人员来解决这个问题。作为参考,这是该方法的重写版本:
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter current in filters)
{
current.OnAuthorization(authorizationContext);
if (authorizationContext.Result != null)
{
break;
}
}
return authorizationContext;
}
最后,为了完成这一切并使一切成为可能,我们的控制器继承自BaseController
,现在返回新的调用者。
public class BaseController : Controller
{
protected override IActionInvoker CreateActionInvoker()
{
return new HasAssignedAcccessActionInvoker();
}
}
答案 1 :(得分:0)
据我所知,您不能以您想要的方式链接[Authorize]
属性,因为它们都必须传递(AND)而不是(OR)行为。但是,将项目合并为一个不会导致您必须执行一些魔术字符串操作,无论您需要传递给它的参数数量。您可以定义自己的Authorize
属性可用的参数集。
public class SuperCoolAuthorize : AuthorizationAttribute
{
public string Parameter1{get;set;}
public string Parameter2{get;set;}
public int Parameter3{get;set;}
public string Parameter4{get;set;}
public override void OnAuthorization(AuthorizationContext filterContext)
{
// your custom behaviour
}
}
在您的控制器/操作方法
上[Authorize(Parameter1 = "Foo", Parameter2 = "Bar", Parameter3 = 47, Parameter4 = string.Empty)
public ActionResult MyControllerAction(){
...
}
A great post on some other considerations关于我在帮助制定此答案时遇到的自定义授权属性。
答案 2 :(得分:0)
public class AuthUserAttribute : AuthorizeAttribute {
public string[] SecurityGroups;
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool valid = false;
var user = UserInformation.Current;
if (user.SecurityGroups.Select(x => x).Intersect(this.SecurityGroups).Any()) {
valid = true;
}
if (user.SecurityGroups.Select(x => x).Intersect(new string[] { "IT Administrators" }).Any()) {
valid = true;
}
return valid;
}
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!this.AuthorizeCore(filterContext.HttpContext)) {
if (UserInformation.Current.SecurityGroups.Count == 0) {
filterContext.Result = new RedirectResult(string.Format("/oa?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
else {
filterContext.Result = new RedirectResult(string.Format("/oa/user/permissions?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
}
else {
base.OnAuthorization(filterContext);
}
}
}
然后我用
装饰[AuthUser(SecurityGroups = new string[] { "Data1", "Data2" })]
public ActionResult ForYourEyesOnly() {
}
我们会看到是否有人抓住了Bond参考。 LOL