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

时间:2010-07-28 21:17:41

标签: c# asp.net asp.net-mvc attributes

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

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

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

所以我在想这种用法......

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

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

对我来说看起来很干净,并且实现起来非常简单,但是我收到以下编译器错误:

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

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

2 个答案:

答案 0 :(得分:4)

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()   
{   
...   
}  

答案 1 :(得分: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 for long post,希望这有帮助。