2010-07-28 77 views
8

我试图从符合一组标准的用户组中保护我的MVC路由。由于MVC似乎使用了很多属性,而Steven Sanderson在他的专业MVC书中使用了一个用于安全扩展性的特性,所以我开始沿着这条路线前进,但是我想根据我正在应用的操作定义上下文规则。将Func作为属性参数传递以保护MVC路线

有些行为只针对员工,有些不行。

某些操作仅适用于company1,有些操作不适用。

所以我想这类型的使用...

[DisableAccess(BlockUsersWhere = u => u.Company != "Acme")] 
public ActionResult AcmeOnlyAction() 
{ 
... 
} 

[DisableAccess(BlockUsersWhere = u => u.IsEmployee == false)] 
public ActionResult EmployeeOnlyAction() 
{ 
... 
} 

看起来很干净,我和真的是很容易实现,但我得到以下编译器错误:

“BlockUsersWhere”不是有效的命名属性参数,因为它不是有效的属性参数类型

显然,您不能使用Func作为属性参数。任何其他建议来解决这个问题或其他的东西,提供了我们在MVC项目中所喜爱的简单用法。

回答

4

丧尸的建议,将工作,但是你必须援引他的SecurityGuard帮手的每一个动作方法的主体。

如果您仍想与声明基于属性的方法去(其中有可以将属性应用到整个控制器的优势),你可以写自己的AuthorizeAttribute

public class CustomAuthorizeAttribute : AuthorizeAttribute { 
    public bool EmployeeOnly { get; set; } 
    private string _company; 

    public string Company { 
     get { return _company; } 
     set { _company = value; } 
    } 


    protected override bool AuthorizeCore(HttpContextBase httpContext) { 
     return base.AuthorizeCore(httpContext) && MyAuthorizationCheck(httpContext); 
    } 

    private bool MyAuthorizationCheck(HttpContextBase httpContext) { 
     IPrincipal user = httpContext.User; 

     if (EmployeeOnly && !VerifyUserIsEmployee(user)) { 
      return false; 
     } 

     if (!String.IsNullOrEmpty(Company) && !VerifyUserIsInCompany(user)) { 
      return false; 
     } 

     return true; 
    } 

    private bool VerifyUserIsInCompany(IPrincipal user) { 
     // your check here 
    } 

    private bool VerifyUserIsEmployee(IPrincipal user) { 
     // your check here 
    } 
} 

,那么你会使用它如下

[CustomAuthorize(Company = "Acme")] 
public ActionResult AcmeOnlyAction() 
{ 
... 
} 

[CustomAuthorize(EmployeeOnly = true)] 
public ActionResult EmployeeOnlyAction() 
{ 
... 
} 
+0

谢谢,这是我想到的第一件事,但用这种方法,我失去了能够在上下文基础上创建独特规则而无需更新属性类。我可能会走下这条路。 – jjr2527 2010-07-29 14:42:38

+0

是的,属性只能传达有限的信息量。他们应该只是关于分配元数据。更复杂的行为属于代码。 – marcind 2010-07-29 18:21:09

1

既然你只能用常量,类型或数组的初始化的属性参数,他们可能不会做,或者至少不会像灵活。

另外,你可以使用类似的东西,当我解决这个问题时想出来的。

这是API:

public static class SecurityGuard 
{ 
    private const string ExceptionText = "Permission denied."; 

    public static bool Require(Action<ISecurityExpression> action) 
    { 
     var expression = new SecurityExpressionBuilder(); 
     action.Invoke(expression); 
     return expression.Eval(); 
    } 

    public static bool RequireOne(Action<ISecurityExpression> action) 
    { 
     var expression = new SecurityExpressionBuilder(); 
     action.Invoke(expression); 
     return expression.EvalAny(); 
    } 

    public static void ExcpetionIf(Action<ISecurityExpression> action) 
    { 
     var expression = new SecurityExpressionBuilder(); 
     action.Invoke(expression); 
     if(expression.Eval()) 
     { 
      throw new SecurityException(ExceptionText); 
     } 
    } 
} 

public interface ISecurityExpression 
{ 
    ISecurityExpression UserWorksForCompany(string company); 
    ISecurityExpression IsTrue(bool expression); 
} 

然后创建一个表达式生成器:

public class SecurityExpressionBuilder : ISecurityExpression 
{ 
    private readonly List<SecurityExpression> _expressions; 

    public SecurityExpressionBuilder() 
    { 
     _expressions = new List<SecurityExpression>(); 
    } 

    public ISecurityExpression UserWorksForCompany(string company) 
    { 
     var expression = new CompanySecurityExpression(company); 
     _expressions.Add(expression); 
     return this; 
    } 

    public ISecurityExpression IsTrue(bool expr) 
    { 
     var expression = new BooleanSecurityExpression(expr); 
     _expressions.Add(expression); 
     return this; 
    } 

    public bool Eval() 
    { 
     return _expressions.All(e => e.Eval()); 
    } 

    public bool EvalAny() 
    { 
     return _expressions.Any(e => e.Eval()); 
    } 
} 

落实安全表达式:

internal abstract class SecurityExpression 
{ 
    public abstract bool Eval(); 
} 

internal class BooleanSecurityExpression : SecurityExpression 
{ 
    private readonly bool _result; 

    public BooleanSecurityExpression(bool expression) 
    { 
     _result = expression; 
    } 

    public override bool Eval() 
    { 
     return _result; 
    } 
} 

internal class CompanySecurityExpression : SecurityExpression 
{ 
    private readonly string _company; 

    public CompanySecurityExpression(string company) 
    { 
     _company = company; 
    } 

    public override bool Eval() 
    { 
     return (WhereverYouGetUser).Company == company; 
    } 
} 

您可以添加尽可能多的自定义表达式为你需要。基础设施是一个有点复杂,但随后的使用是非常简单的:

public ActionResult AcmeOnlyAction() 
{ 
    SecurityGuard.ExceptionIf(s => s.UserWorksForCompany("Acme")); 
} 

你也可以连接的表达,并(使用SecurityGuard.Require())鉴于使用它作为一个条件来回例子。

Sry很长的帖子,希望这有助于。