2010-02-04 86 views
15

所以我正在閱讀有關登錄循環的另一個問題,當您有用戶登錄時,設置爲返回到他們在登錄後可能無法訪問的URL(即管理頁面,並且用戶使用正常登錄帳戶)。ASP.Net MVC如何確定用戶是否可以訪問URL?

WebForms下的解決方案似乎是利用UrlAuthorizationModule.CheckUrlAccessForPrincipal方法。但是,這對於轉到使用授權屬性保護的操作方法的URL不起作用。我想我可以計算出URL指向哪種方法並反映出來解決我的問題 - 但我似乎無法弄清楚如何從路由表中獲取這些信息。

任何人都曾與此合作過,或有解決方案嗎?如果我可以從URL中獲取路由信息,我認爲我可以將其餘的工作,但如果任何人有一個通用的解決方案 - 即。一些隱藏的方法類似於之前提到的用於MVC的方法,那麼這也是完全可怕的。

我不是問如何檢查用戶是否有權訪問指定的控制器/操作對。我首先需要弄清楚如何從基於URL的RouteTable獲取Controller/Action對。所有背景故事的原因是,如果MVC確實存在相當於UrlAuthorizationModule.CheckUrlAccessForPrincipal的情況。

回答

1

什麼是你正在試圖解決這個問題?聽起來你可能正在走向一條複雜解決方案的道路,而該解決方案可以使用簡單的解決方案。

如果用戶在登錄後沒有權限訪問該頁面,是否希望未登錄的用戶轉到一個頁面,而登錄的用戶轉到其他頁面?

如果是這種情況,我可能會試圖爲這種場景創建另一個控制器,並在用戶無法訪問的任何地方重定向到該控制器。或者如果您使用自己的基本控制器,我會把功能放在那裏。

然後控制器可以呈現所需的視圖。例如,如果一個未登錄的用戶試圖訪問一個頁面,他們可能會被重定向到一個通用的錯誤頁面。如果用戶登錄,他們可能會被重定向到未經授權的頁面。

這與Robert的回答非常相似。

下面是基本控制器的基本框架。

public BaseController: Controller 
{ 

... // Some code 

    public ActionResult DisplayErrorPage() 
    { 
     // Assumes you have a User object with a IsLoggedIn property 
     if (User.IsLoggedIn())  
      return View("NotAuthorized"); 

     // Redirect user to login page 
     return RedirectToAction("Logon", "Account"); 
    } 

} 

然後在讓說一個AdminController(從BaseController繼承)行動

public ActionResult HighlyRestrictedAction() 
{ 
    // Assumes there is a User object with a HasAccess property 
    if (User.HasAccess("HighlyRestrictedAction") == false) 
     return DisplayErrorPage(); 

    // At this point the user is logged in and has permissions 
    ... 
} 
+0

我現在會接受,因爲它現在似乎是最好的解決方案。我不確定我會實際使用這個。我真的很喜歡不必將這種邏輯嵌入到控制器中的想法 - 我也想避免額外的重定向。 – kastermester 2010-02-11 01:07:11

1

這可能是要健全爭議,但我在每個控制器方法的開始檢查安全,裏面的方法:

public class ProductController : Controller 
{ 
    IProductRepository _repository 

    public ActionResult Details(int id) 
    { 
     if(!_repository.UserHasAccess(id)) 
      return View("NotAuthorized"); 

     var item = _repository.GetProduct(id); 

     if (item == null) 
      return View("NotFound"); 

     return View(item); 
    } 
} 

我之所以不使用[Authorize]屬性。這就是你無法在運行時將ID或任何其他標識信息傳遞給屬性。

+0

謝謝,但我不知道這種做法會怎樣幫助我 - 看我離開jfar的意見。 – kastermester 2010-02-04 13:31:24

5

我移植和砍死從MvcSitemap此代碼:

public static class SecurityTrimmingExtensions 
{ 

    /// <summary> 
    /// Returns true if a specific controller action exists and 
    /// the user has the ability to access it. 
    /// </summary> 
    /// <param name="htmlHelper"></param> 
    /// <param name="actionName"></param> 
    /// <param name="controllerName"></param> 
    /// <returns></returns> 
    public static bool HasActionPermission(this HtmlHelper htmlHelper, string actionName, string controllerName) 
    { 
     //if the controller name is empty the ASP.NET convention is: 
     //"we are linking to a different controller 
     ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) 
               ? htmlHelper.ViewContext.Controller 
               : GetControllerByName(htmlHelper, controllerName); 

     var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo); 

     var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType()); 

     var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName); 

     return ActionIsAuthorized(controllerContext, actionDescriptor); 
    } 


    private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     if (actionDescriptor == null) 
      return false; // action does not exist so say yes - should we authorise this?! 

     AuthorizationContext authContext = new AuthorizationContext(controllerContext); 

     // run each auth filter until on fails 
     // performance could be improved by some caching 
     foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters) 
     { 
      authFilter.OnAuthorization(authContext); 

      if (authContext.Result != null) 
       return false; 
     } 

     return true; 
    } 

    private static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName) 
    { 
     // Instantiate the controller and call Execute 
     IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory(); 

     IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName); 

     if (controller == null) 
     { 
      throw new InvalidOperationException(

       String.Format(
        CultureInfo.CurrentUICulture, 
        "Controller factory {0} controller {1} returned null", 
        factory.GetType(), 
        controllerName)); 

     } 

     return (ControllerBase)controller; 
    } 

它可以使用一些緩存,但我的情況,這是一個不成熟的優化。

+0

我的問題是我有一個URL,而不是一個控制器和一個動作。你在這裏寫的代碼是我相信我可以自己做的東西 - 儘管感謝分享它,因爲我可能需要這樣的東西。但問題是 - 我如何從URL中將Controller + Action從路由表中提取出來? 爲什麼我寫了很多關於其他的東西的原因是因爲我懷疑有可能實際上是一個方法,打電話告訴我這一切在1分走。 – kastermester 2010-02-04 13:30:03

+0

@kastermester:爲此,您必須在執行控制器方法之前攔截請求。有關如何做到這一點的想法,看看這裏:http://stackoverflow.com/questions/2122459/how-malleable-are-the-conventions-in-asp-net-mvc/2122521#2122521 – 2010-02-04 17:37:20

+0

正是我需要的。我一直在尋找這個高和低。非常感謝! – 2010-05-07 08:08:24

0

爲什麼不將控制器方法歸入安全要求。

我寫了一個屬性要做到這一點,如下所示:

public class RequiresRoleAttribute : ActionFilterAttribute 
     { 
      public string Role { get; set; } 

      public override void OnActionExecuting(ActionExecutingContext filterContext) 
      { 
       if (string.IsNullOrEmpty(Role)) 
       { 
        throw new InvalidOperationException("No role specified."); 
       } 


       if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
       { 
        filterContext.HttpContext.Response.Redirect(loginUrl, true); 
       } 
       else 
       { 
        bool isAuthorised = filterContext.HttpContext.User.IsInRole(this.Role); 

        << Complete Logic Here >> 



       } 
      }  
     } 
+0

我的問題是這樣的:一個用戶去我登錄頁面。在輸入時,「返回」url被設置並被攜帶在查詢字符串中。讓我們假設一下,該URL指向該網站的管理員部分。用戶使用普通帳戶登錄。現在使用這種代碼(我是)用戶將被重定向到登錄頁面 - 但他已經登錄了!我可以簡單地重定向到默認頁面 - 但隨後每個人都會到達那裏 - 也是未經認證的人員。我非常想在登錄後重定向之前驗證這一點。 – kastermester 2010-02-05 21:28:30

1

在我的應用程序創建自AuthorizeAttribute派生的自定義過濾器,因此,任何未經授權的訪問只會去存取遭拒頁。對於鏈接,我用自定義幫助器Html.SecureLink替換Html.ActionLink。在這個幫助器擴展中,我檢查這個用戶的角色訪問控制器/對數據庫的操作。如果他/她是否有授權,反向鏈路否則返回與專項說明鏈接文字(可以是圖像/色彩/ JS)

8

jfar的回答上述更新的MVC 4:

public static class SecurityCheck 
{ 
    public static bool ActionIsAuthorized(string actionName, string controllerName) 
    { 
     IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory(); 
     ControllerBase controller = factory.CreateController(HttpContext.Current.Request.RequestContext, controllerName) as ControllerBase; 
     var controllerContext = new ControllerContext(HttpContext.Current.Request.RequestContext, controller); 
     var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType()); 
     var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName); 
     AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor); 
     foreach (var authAttribute in actionDescriptor.GetFilterAttributes(true).Where(a => a is AuthorizeAttribute).Select(a => a as AuthorizeAttribute)) 
     { 
      authAttribute.OnAuthorization(authContext); 
      if (authContext.Result != null) 
       return false; 
     } 
     return true; 
    } 
} 
+0

或者「foreach(var authAttribute in actionDescriptor.GetFilterAttributes(true).OfType ())」 - 爲apantry道歉。 – 2013-12-04 14:33:17

0

我只花了一些時間來實現@ jfar的解決方案(更新它的非棄用GetFilters()版本),然後我意識到,我可以跳過這件事。

在我的情況

(我假設大多數情況下)我有一個自定義AuthorizationAttribute來實現網站的授權,後者又調用我的授權服務,以使實際的訪問級別determinenation。

所以在我的HTML輔助生成菜單鏈接,我跳過權auth服務:

@Html.MenuItem(@Url, "icon-whatever", "TargetController", "TargetAction") 

public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper, UrlHelper url,string iconCss, string targetController, string targetAction) 
{ 
    var auth = IoC.Resolve<IClientAuthorizationService>().Authorize(targetController, targetAction); 
    if (auth == AccessLevel.None) 
     return MvcHtmlString.Create(""); 

*用戶在客戶端身份驗證服務內確定

public string GetUser() { 
    return HttpContext.Current.User.Identity.Name; 
} 

*還可以添加一些行爲只讀訪問。這很好,因爲我的身份驗證服務負責緩存,所以我不必擔心性能。

相關問題