ASP.Net MVC如何确定用户是否可以访问URL?

问题描述:

所以我正在阅读有关登录循环的另一个问题,当您有用户登录时,设置为返回到他们在登录后可能无法访问的URL(即管理页面,并且用户使用正常登录帐户)。ASP.Net MVC如何确定用户是否可以访问URL?

WebForms下的解决方案似乎是利用UrlAuthorizationModule.CheckUrlAccessForPrincipal方法。但是,这对于转到使用授权属性保护的操作方法的URL不起作用。我想我可以计算出URL指向哪种方法并反映出来解决我的问题 - 但我似乎无法弄清楚如何从路由表中获取这些信息。

任何人都曾与此合作过,或有解决方案吗?如果我可以从URL中获取路由信息,我认为我可以将其余的工作,但如果任何人有一个通用的解决方案 - 即。一些隐藏的方法类似于之前提到的用于MVC的方法,那么这也是完全可怕的。

我不是问如何检查用户是否有权访问指定的控制器/操作对。我首先需要弄清楚如何从基于URL的RouteTable获取Controller/Action对。所有背景故事的原因是,如果MVC确实存在相当于UrlAuthorizationModule.CheckUrlAccessForPrincipal的情况。

什么是你正在试图解决这个问题?听起来你可能正在走向一条复杂解决方案的道路,而该解决方案可以使用简单的解决方案。

如果用户在登录后没有权限访问该页面,是否希望未登录的用户转到一个页面,而登录的用户转到其他页面?

如果是这种情况,我可能会试图为这种场景创建另一个控制器,并在用户无法访问的任何地方重定向到该控制器。或者如果您使用自己的基本控制器,我会把功能放在那里。

然后控制器可以呈现所需的视图。例如,如果一个未登录的用户试图访问一个页面,他们可能会被重定向到一个通用的错误页面。如果用户登录,他们可能会被重定向到未经授权的页面。

这与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

这可能是要健全争议,但我在每个控制器方法的开始检查安全,里面的方法:

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

我移植和砍死从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://*.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

为什么不将控制器方法归入安全要求。

我写了一个属性要做到这一点,如下所示:

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

在我的应用程序创建自AuthorizeAttribute派生的自定义过滤器,因此,任何未经授权的访问只会去存取遭拒页。对于链接,我用自定义帮助器Html.SecureLink替换Html.ActionLink。在这个帮助器扩展中,我检查这个用户的角色访问控制器/对数据库的操作。如果他/她是否有授权,反向链路否则返回与专项说明链接文字(可以是图像/色彩/ JS)

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

我只花了一些时间来实现@ 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; 
} 

*还可以添加一些行为只读访问。这很好,因为我的身份验证服务负责缓存,所以我不必担心性能。