2011-11-08 114 views
2

我有一個安全應用程序,每個操作都有一個Authorize屬性。基於控制器/操作的MVC UI修剪授權屬性

[Authorize(Roles = "Role1,Role2")] 
public ActionResult MyAction(int id) 
{ 
    return View(); 
} 

在我的用戶界面中,我有這些控制器/操作的鏈接。我想創建一個鏈接一個自定義的HtmlHelper接受控制器和動作名稱:

@Html.SecuredLink("Click Me", "MyAction", "MyController"); 

,這將決定耐候來表現自己或不基於用戶是否有權限給定的行動:

public static MvcHtmlString SecuredLink(this HtmlHelper helper, string text, string action, string controller) 
{   
    var userId = Membership.GetUserId(); 

    var userHasRightsToThisAction = IsActionAccessibleToUser(helper.ViewContext.RequestContext.HttpContext, controller, action); // <- How would this work? 

    if (userHasRightsToThisAction) 
    { 
     // Render Link 
     // ... 
    } 
} 

我一直無法找到一種方法來輕鬆測試授權狀態代碼中的操作。

+0

你能澄清爲什麼不能正常渲染鏈接,然後在自定義的AuthorizeAttribute中重定向?不過,我認爲你應該首先查看User.Identity對象,然後確定授權名稱,然後決定是否渲染。 – BigMike

+0

我不希望用戶能夠點擊該鏈接,如果他們沒有對其導致的頁面的權利。 – CodeGrue

回答

3

好的找到了解決方案。圍繞我知道做安全修整的MvcSiteMap挖後,我發現這篇文章吧:

http://blog.maartenballiauw.be/post/2008/08/29/Building-an-ASPNET-MVC-sitemap-provider-with-security-trimming.aspx

我用了一下這個代碼,稍加修改,創建,讓我想要的結果的方法:

/// <summary> 
    /// Determine if a controller/action is accessible for a user 
    /// </summary> 
    /// <param name="context">Current HttpContext</param> 
    /// <param name="controllerName">Target controller</param> 
    /// <param name="actionName">Target action</param> 
    /// <returns>True/false if the action is accessible</returns> 
    public static bool IsActionAccessibleToUser(HttpContextBase context, string controllerName, string actionName) 
    { 
     // Find current handler 
     MvcHandler handler = context.Handler as MvcHandler; 

     if (handler != null) 
     { 
      // try to figure out the controller class 
      IController controller = null; 
      try 
      { 
       controller = ControllerBuilder.Current.GetControllerFactory().CreateController(handler.RequestContext, controllerName);      
      } 
      catch (System.Web.HttpException e) 
      { 
       throw new Exception("The controller '" + controllerName + "Controller' was not found.", e); 
      } 

      // Find all AuthorizeAttributes on the controller class and action method 
      object[] controllerAttributes = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true); 
      object[] actionAttributes = controller.GetType().GetMethod(actionName).GetCustomAttributes(typeof(AuthorizeAttribute), true); 

      // No attributes, then the action is open to all 
      if (controllerAttributes.Length == 0 && actionAttributes.Length == 0) return true; 

      // Find out current principal 
      IPrincipal principal = handler.RequestContext.HttpContext.User; 

      // Do we pass the roles for the controller? 
      string roles = ""; 
      if (controllerAttributes.Length > 0) 
      { 
       AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute; 
       roles = attribute.Roles; 

       if (!PassRoleValidation(principal, roles)) return false; 
      } 

      // Do we pass the roles for the action? 
      if (actionAttributes.Length > 0) 
      { 
       AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute; 
       roles = attribute.Roles; 

       if (!PassRoleValidation(principal, roles)) return false; 
      } 

      return true; 
     } 

     return false; 
    } 

    private static bool PassRoleValidation(IPrincipal principal, string roles) 
    { 
     // no roles, then all we need to be is authenticated 
     if (string.IsNullOrEmpty(roles) && principal.Identity.IsAuthenticated) return true; 

     string[] roleArray = roles.Split(','); 

     // if role contains "*", it's open to all 
     if (roleArray.Any(role => role == "*")) return true; 

     // Determine if the current user is allowed to access the current node 
     if (roleArray.Any(principal.IsInRole)) return true; 

     return false; 
    } 
+0

如果您的控制器有兩個具有相同名稱的操作(例如,一個用於POST,另一個用於GET請求),則這不起作用。 'controller.GetType()。GetMethod(actionName)'會拋出'AmbiguousMatchException'。 –

0

好,快速和灰塵的解決方案:

準備一個功能構建的URL服務器端

這樣的事情可能會是最好的選擇:

public static string GetUrl(string Action, string Controller, object RouteValues) { 
    UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext); 
    return Url.Action(Action, Controller, RouteValues); 
} 

在你的助手,獲得用戶身份驗證信息並返回構建的url或string.Empty。

public static string SecureLink(this HtmlHelper helper, string Action, string Controller, object RouteValues) 
{ 
    YourUserObject LoggedUser = /* Whatever you need to obtain your UserId */ 
    if (LoggedUser.IsSuperUser) { 
    return GetUrl(Action, Controller, RouteValues); 
    } 
    return string.empty; 
} 

如果你的結果是HTML編碼只是使用MvcHtmlString代替字符串作爲返回值。 否則要小心,您可能需要使用@Html.Raw來發射它。

PS:很明顯,我還沒有完全加代<a href .../>在此示例代碼,是由你決定哪些參數是需要(將其添加到助手的簽名),我通常複製其他@Html傭工的簽名(所以名稱,值和HtmlAttributes的列表)。

+0

你說什麼「LoggedUser.IsSuperUser」,我想根據每個MVC Action的Authorize decortations來確定權限。這是我正在努力的部分。所以如果Action有「授權(Roles =」Associate「),我想以某種方式確定它,並讓系統告訴我,如果當前用戶是」Associate「,或者更好,他們是否有資格執行Action。 – CodeGrue

+0

我真的懷疑你可以從Helper中訪問Attributes屬性,在執行Action之後,Helper被執行用於渲染View。你可以嘗試構建一個javascript(客戶端)工件來啓用/禁用Links,但是那樣會恕我直言,在這種情況下最好讓鏈接點擊並處理未授權的服務器端。 – BigMike

+0

好吧,我正在測試一個不同的控制器/動作,不一樣。所以我在A/B上,鏈接轉到A/C,所以我想問A/C,如果用戶有權去那裏。所以我並不試圖從內部測試我所處理的控制器/操作。 – CodeGrue