将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,希望这会有所帮助。