ASP.Net MVC, как определить, может ли пользователь получить доступ к URL? - PullRequest
16 голосов
/ 04 февраля 2010

Итак, я читал еще один вопрос, касающийся цикла входа в систему, когда пользователь входит в систему, настроен на возврат к URL-адресу, к которому у него может не быть доступа после входа в систему (т. Е. Странице администратора, и пользователь входит в систему с обычный аккаунт).

Решение в WebForms, похоже, заключается в использовании метода UrlAuthorizationModule.CheckUrlAccessForPrincipal. Однако это не работает для URL-адресов, идущих к методам действий, защищенным с помощью атрибута Authorize. Я подумал, что смогу решить, на какой метод указывал URL-адрес, и подумать над ним, чтобы решить мою проблему - но я не могу понять, каким образом я получаю эту информацию из таблицы маршрутизации.

Кто-нибудь когда-либо работал с этим, или есть решение для этого? Если бы я мог просто получить информацию о маршруте из URL, я думаю, что я мог бы решить все остальное, но если у кого-то есть общее решение - т.е. какой-то скрытый метод, похожий на ранее упомянутый метод для MVC, тогда это также было бы совершенно потрясающе.

Я не спрашиваю, как проверить, имеет ли пользователь доступ к указанной паре контроллер / действие . Прежде всего, мне нужно выяснить, как получить пару Controller / Action из RouteTable на основе URL. Причина всей истории вопроса в том, что действительно существует эквивалент UrlAuthorizationModule.CheckUrlAccessForPrincipal для MVC.

Ответы [ 7 ]

8 голосов
/ 16 октября 2013

Ответ Джона Фаррелла (jfar) (класс SecurityTrimmingExtensions) обновлен для 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;
    }
}
5 голосов
/ 04 февраля 2010

Я портировал и взломал этот код из 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;
    }

Он мог бы использовать некоторое кэширование, но для моего случая это было преждевременной оптимизацией.

1 голос
/ 29 июля 2010

В моем приложении я создал собственный фильтр, полученный из AuthorizeAttribute, поэтому любой несанкционированный доступ просто перейдет на страницу AccessDenied. Для ссылок я заменяю Html.ActionLink на собственный вспомогательный Html.SecureLink. В этом вспомогательном расширении я проверяю доступ ролей этого пользователя к контроллеру / действию в отношении базы данных. Если у него есть полномочия, верните ссылку, в противном случае верните текст ссылки со специальными замечаниями (это может быть изображение / раскраска / js)

1 голос
/ 11 февраля 2010

Какую проблему вы пытаетесь решить?Похоже, вы, возможно, идете по пути к сложному решению, в котором вместо этого можно было бы использовать простое решение.

Если у пользователя нет прав доступа к странице после входа в систему, хотите войти в систему?пользователи переходят на одну страницу, а вошедшие в систему пользователи переходят на другую страницу?

Если это так, у меня может возникнуть желание создать еще один контроллер для таких сценариев и перенаправить на этот контроллер везде, где пользователь этого не делает.иметь доступ.Или, если вы используете свой собственный базовый контроллер, я бы поместил туда функциональность.

Тогда контроллер может представить желаемый вид.Например, если не авторизованный пользователь пытается получить доступ к странице, он может быть перенаправлен на общую страницу ошибки.Если пользователь вошел в систему, он может быть перенаправлен на несанкционированную страницу.

Это очень похоже на ответ Роберта.

Вот базовый скелет для базового контроллера.

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
    ...
}
1 голос
/ 04 февраля 2010

Это может показаться спорным, но я проверяю безопасность в начале каждого метода контроллера, внутри метода:

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], заключается в том, что вы не можете передать идентификатор или любую другую идентифицирующую информацию атрибуту во время выполнения.

0 голосов
/ 09 января 2014

Я просто потратил некоторое время на реализацию решения @ jfar (обновив его для версии GetFilters (), не подлежащей устареванию), а затем понял, что могу пропустить все это.

в моем случае (и я предполагаю, что в большинстве случаев) у меня есть собственный атрибут AuthorizationAttribute для реализации авторизации сайта, который, в свою очередь, вызывает мою службу авторизации для определения фактического уровня доступа.

Итак, в моем html помощнике для генерации ссылок меню я пропустил прямо к сервису аутентификации:

@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;
}

* также может добавить некоторое поведение для доступа только для чтения. это хорошо, потому что мой сервис аутентификации заботится о кэшировании, поэтому мне не нужно беспокоиться о производительности.

0 голосов
/ 05 февраля 2010

Почему бы не приписать методы вашего контроллера требованиям безопасности.

Я написал атрибут для этого следующим образом:

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



                }  
            }      
        }
...