2010-07-28 46 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很長的帖子,希望這有助於。

相關問題