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

kas*_*ter 16 asp.net-mvc authorization

当我有用户登录时,我正在阅读有关登录循环的另一个问题,设置为返回登录后可能无法访问的URL(即管理员页面,用户使用普通帐户登录) .

WebForms下的解决方案似乎是利用该UrlAuthorizationModule.CheckUrlAccessForPrincipal方法.但是,对于使用授权属性保护的Action方法的URL不起作用.我想我可以弄清楚URL指向哪个方法并反映它以解决我的问题 - 但我似乎无法弄清楚如何从路由表中获取此信息.

有人曾经使用过这个,或者有解决方案吗?如果我能从URL获取路由信息,我想我可以完成其余的工作,但是如果有人有一个通用的解决方案 - 即.一些隐藏的方法类似于之前提到的MVC,然后这也是非常棒的.

我不是问如何检查用户是否有权访问指定的Controller/Action对.我首先需要弄清楚如何根据URL从RouteTable获取Controller/Action对.所有背景故事的原因都在于确实存在与UrlAuthorizationModule.CheckUrlAccessForPrincipalMVC 相当的情况.

bjr*_*son 9

针对MVC 4更新了上面的jfar答案:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 或“ foreach(actionDescriptor.GetFilterAttributes(true).OfType <AuthorizeAttribute>()中的var authAttribute)”-对于该学徒的道歉。 (2认同)

Joh*_*ell 6

我从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;
    }
Run Code Online (Sandbox Code Playgroud)

它可以使用一些缓存,但对于我的情况,这是一个过早的优化.

  • 看起来ActionDescriptor.GetFilters()现在已经过时了.不确定此代码如何与System.Web.Mvc.FilterProviders一起使用. (2认同)

37S*_*ars 1

您想要解决的问题是什么?听起来您可能会走上一条通往复杂解决方案的道路,而实际上可以使用简单的解决方案。

如果用户在登录后无权访问该页面,您是否希望未登录的用户转到一个页面,而登录的用户转到另一个页面?

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

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

这与罗伯特的回答非常相似。

这是基本控制器的基本框架。

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

}
Run Code Online (Sandbox Code Playgroud)

然后我们说一个 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
    ...
}
Run Code Online (Sandbox Code Playgroud)