2010-07-28 26 views
8

Tôi đang cố gắng bảo mật các tuyến đường MVC của mình từ một nhóm người dùng đáp ứng một bộ tiêu chí. Vì MVC dường như sử dụng các thuộc tính khá nhiều và Steven Sanderson sử dụng một thuộc tính để mở rộng bảo mật trong cuốn sách MVC chuyên nghiệp của mình, tôi bắt đầu đi xuống tuyến này, nhưng tôi muốn xác định quy tắc dựa trên hành động mà tôi áp dụng nó.Truyền Func làm tham số thuộc tính để bảo đảm các tuyến MVC

Một số hành động chỉ dành cho nhân viên, một số thì không.

Một số hành động chỉ dành cho công ty1, một số hành động không phải là.

Vì vậy, tôi đã suy nghĩ kiểu này sử dụng ...

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

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

Trông khá sạch sẽ với tôi và thực sự là khá dễ dàng để thực hiện, nhưng tôi nhận được lỗi biên dịch sau:

'BlockUsersWhere' không phải là đối số thuộc tính được đặt tên hợp lệ vì nó không phải là thông số thuộc tính hợp lệ loại

Rõ ràng bạn không thể sử dụng đối số Func làm đối số. Bất kỳ đề xuất nào khác để giải quyết vấn đề này hay một thứ gì đó khác cung cấp cách sử dụng đơn giản mà chúng tôi đã yêu thích trong các dự án MVC của chúng tôi?

Trả lời

4

Đề xuất của Necros sẽ hoạt động, tuy nhiên bạn sẽ phải gọi người trợ giúp SecurityGuard của mình trong cơ thể của mọi phương thức hành động.

Nếu bạn vẫn muốn đi với cách tiếp cận thuộc tính dựa trên khai báo (trong đó có các lợi thế mà bạn có thể áp dụng các thuộc tính cho toàn bộ điều khiển), bạn có thể viết riêng của bạn 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 
    } 
} 

Sau đó, bạn sẽ sử dụng nó như sau

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

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

Cảm ơn, đây là điều đầu tiên tôi nghĩ nhưng với cách tiếp cận này, tôi mất khả năng tạo quy tắc duy nhất trên cơ sở theo ngữ cảnh mà không phải cập nhật lớp thuộc tính. Tôi có thể sẽ đi xuống con đường này. – jjr2527

+0

Vâng, các thuộc tính chỉ có thể truyền đạt một lượng thông tin giới hạn. Họ thực sự chỉ nên chỉ định về siêu dữ liệu. Hành vi phức tạp hơn thuộc về mã. – marcind

1

Vì bạn chỉ có thể sử dụng hằng số, loại hoặc bộ khởi tạo mảng trong tham số thuộc tính, chúng có thể sẽ không hoạt động hoặc ít nhất sẽ không linh hoạt.

Ngoài ra, bạn có thể sử dụng một cái gì đó tương tự như tôi đã đưa ra khi giải quyết vấn đề này.

Đây là 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); 
} 

Sau đó tạo một xây dựng biểu thức:

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()); 
    } 
} 

Thực hiện các biểu thức bảo mật:

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; 
    } 
} 

Bạn có thể thêm bao nhiêu biểu thức tùy chỉnh như bạn nhu cầu. Cơ sở hạ tầng là một chút phức tạp, nhưng sau đó sử dụng là thực sự đơn giản:

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

Bạn cũng có thể chuỗi biểu thức, và sử dụng nó như một điều kiện theo quan điểm fro dụ (sử dụng SecurityGuard.Require()).

Sry cho bài đăng dài, hy vọng điều này sẽ hữu ích.

Các vấn đề liên quan