将Func作为属性参数传递以保护MVC路由

我正在尝试从满足一系列标准的一组用户中保护我的MVC路由。 由于MVC似乎使用了很多属性而且Steven Sanderson在他的专业MVC书中使用了一个安全可扩展性,所以我开始沿着这条路走下去,但我想根据我应用它的动作来定义规则。

有些行为仅适用于员工,有些则不适用。

有些行为仅适用于公司1,有些则不适用。

所以我在想这种用法……

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

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

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

显然你不能使用Func作为属性参数。 任何其他建议可以解决这个问题或其他提供我们在MVC项目中喜欢的简单用法的建议吗?

Necros的建议可行,但是你必须在每个动作方法的主体中调用他的SecurityGuard助手。

如果您仍然希望使用基于声明属性的方法(具有可以将属性应用于整个Controller的优点),您可以编写自己的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() { ... } 

由于您只能在属性参数中使用常量,类型或数组初始值设定项,因此它们可能不会这样做,或者至少不会那么灵活。

或者,您可以使用我在解决此问题时提出的类似内容。

这是API:

 public static class SecurityGuard { private const string ExceptionText = "Permission denied."; public static bool Require(Action action) { var expression = new SecurityExpressionBuilder(); action.Invoke(expression); return expression.Eval(); } public static bool RequireOne(Action action) { var expression = new SecurityExpressionBuilder(); action.Invoke(expression); return expression.EvalAny(); } public static void ExcpetionIf(Action 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 _expressions; public SecurityExpressionBuilder() { _expressions = new List(); } 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 for long post,希望这会有所帮助。