Przekierowanie z atrybutu filtru akcji

139

Jaki jest najlepszy sposób na przekierowanie w pliku ActionFilterAttribute. Mam ActionFilterAttributewezwanie IsAuthenticatedAttributeFilteri sprawdziłem wartość zmiennej sesji. Jeśli zmienna ma wartość false, chcę, aby aplikacja przekierowała do strony logowania. Wolałbym przekierować przy użyciu nazwy trasy, SystemLoginjednak w tym momencie każda metoda przekierowania byłaby w porządku.

ryanzec
źródło

Odpowiedzi:

187

Ustaw filterContext.Result

Z nazwą trasy:

filterContext.Result = new RedirectToRouteResult("SystemLogin", routeValues);

Możesz też zrobić coś takiego:

filterContext.Result = new ViewResult
{
    ViewName = SharedViews.SessionLost,
    ViewData = filterContext.Controller.ViewData
};

Jeśli chcesz użyć RedirectToAction:

Możesz utworzyć publiczną RedirectToActionmetodę na swoim kontrolerze ( najlepiej na swoim kontrolerze podstawowym ), która po prostu wywoła chroniony RedirectToActionprzed System.Web.Mvc.Controller. Dodanie tej metody pozwala na publiczne wywołanie Twojego RedirectToAction z filtra.

public new RedirectToRouteResult RedirectToAction(string action, string controller)
{
    return base.RedirectToAction(action, controller);
}

Wtedy twój filtr wyglądałby mniej więcej tak:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var controller = (SomeControllerBase) filterContext.Controller;
    filterContext.Result = controller.RedirectToAction("index", "home");
}
CRice
źródło
8
To działa, ale czy nie powinna być dostępna metoda RedirectToAction?
Ben Mills,
@BenMills jest jednak po to, protectedabyś nie miał do niego dostępu z poziomu filtra.
James,
10
Moje pytanie brzmi teraz, dlaczego Microsoft zdecydował, że ten filtr protectedmusi być jakieś rozsądne wyjaśnienie? Czuję się bardzo nieprzyjemnie definiując tę ​​dostępność RedirectToActionbez zrozumienia, dlaczego została ona zamknięta w pierwszej kolejności.
Matthew Marlin
2
@MatthewMarlin - Zobacz odpowiedź Syakura, aby uzyskać właściwą odpowiedź na przekierowanie do działania. Masz rację, że nie powinieneś wywoływać kontrolera bezpośrednio z filtru akcji - to jest definicja ścisłego sprzężenia.
NightOwl888
1
@Akbari Czy próbowałeś ustawić właściwość Order atrybutu? Również FilterScope wpłynie na kolejność wykonania.
CRice
79

Alternatywnie do przekierowania, jeśli wywołuje Twój własny kod, możesz użyć tego:

actionContext.Result = new RedirectToRouteResult(
    new RouteValueDictionary(new { controller = "Home", action = "Error" })
);

actionContext.Result.ExecuteResult(actionContext.Controller.ControllerContext);

Nie jest to zwykłe przekierowanie, ale daje podobny wynik bez zbędnych kosztów ogólnych.

Syakur Rahman
źródło
Pomogłeś mi. Dzięki!
Edgar Salazar
25
Zauważ, że nie powinieneś wywoływać actionContext.Result.ExecuteResultz poziomu swojego filtra akcji - MVC zrobi to automatycznie po uruchomieniu filtru akcji (pod warunkiem, że actionContext.Resultnie jest null).
NightOwl888
12

Używam MVC4, zastosowałem następujące podejście do przekierowania niestandardowego ekranu html po naruszeniu autoryzacji.

Rozszerz AuthorizeAttributepowiedz CutomAuthorizer zastąpienie OnAuthorizationiHandleUnauthorizedRequest

Zarejestruj CustomAuthorizerw RegisterGlobalFilters.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{

    filters.Add(new CustomAuthorizer());
}

po zidentyfikowaniu unAuthorizedwywołania dostępu HandleUnauthorizedRequesti przekierowaniu do odpowiedniej akcji kontrolera, jak pokazano poniżej.


public class CustomAuthorizer : AuthorizeAttribute
{

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        bool isAuthorized = IsAuthorized(filterContext); // check authorization
        base.OnAuthorization(filterContext);
        if (!isAuthorized && !filterContext.ActionDescriptor.ActionName.Equals("Unauthorized", StringComparison.InvariantCultureIgnoreCase)
            && !filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.Equals("LogOn", StringComparison.InvariantCultureIgnoreCase))
        {

            HandleUnauthorizedRequest(filterContext);

        }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result =
       new RedirectToRouteResult(
           new RouteValueDictionary{{ "controller", "LogOn" },
                                          { "action", "Unauthorized" }

                                         });

    }
}
user2834076
źródło
9

Wygląda na to, że chcesz ponownie zaimplementować lub rozszerzyć AuthorizeAttribute. Jeśli tak, upewnij się, że dziedziczisz to, a nie ActionFilterAttribute, aby umożliwić ASP.NET MVC wykonanie większej ilości pracy za Ciebie.

Ponadto chcesz się upewnić, że autoryzujesz się przed wykonaniem jakiejkolwiek rzeczywistej pracy w metodzie akcji - w przeciwnym razie jedyną różnicą między zalogowanym a niezalogowanym będzie to, jaką stronę zobaczysz po zakończeniu pracy.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Do whatever checking you need here

        // If you want the base check as well (against users/roles) call
        base.OnAuthorization(filterContext);
    }
}

Jest dobre pytanie z odpowiedzią zawierającą więcej szczegółów na SO.

Tomas Aschan
źródło
5

Wypróbuj następujący fragment, powinien być całkiem jasny:

public class AuthorizeActionFilterAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(FilterExecutingContext filterContext)
  {
    HttpSessionStateBase session = filterContext.HttpContext.Session;
    Controller controller = filterContext.Controller as Controller;

    if (controller != null)
    {
      if (session["Login"] == null)
      {
        filterContext.Cancel = true;
        controller.HttpContext.Response.Redirect("./Login");
      }
    }

    base.OnActionExecuting(filterContext);
  }
}
Muhammad Soliman
źródło
To zadziałało dla mnie, musiałem sprawdzić wartości ciągu zapytania, jeśli jakikolwiek użytkownik próbuje zmienić wartości ciągu zapytania i próbuje uzyskać dostęp do danych, które nie są dla niego / niej autoryzowane, a ja przekierowuję je do nieautoryzowanej strony wiadomości, używając ActionFilterAttribute.
Sameer
3

Oto rozwiązanie, które bierze również pod uwagę, jeśli używasz żądań Ajax.

using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace YourNamespace{        
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeCustom : ActionFilterAttribute {
        public override void OnActionExecuting(ActionExecutingContext context) {
            if (YourAuthorizationCheckGoesHere) {               
                string area = "";// leave empty if not using area's
                string controller = "ControllerName";
                string action = "ActionName";
                var urlHelper = new UrlHelper(context.RequestContext);                  
                if (context.HttpContext.Request.IsAjaxRequest()){ // Check if Ajax
                    if(area == string.Empty)
                        context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(controller, action))}');</script>");
                    else
                        context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(area, controller, action))}');</script>");
                } else   // Non Ajax Request                      
                    context.Result = new RedirectToRouteResult(new RouteValueDictionary( new{ area, controller, action }));             
            }
            base.OnActionExecuting(context);
        }
    }
}
Mikrofon
źródło
1

To działa dla mnie (asp.net core 2.1)

using JustRide.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MyProject.Web.Filters
{
    public class IsAuthenticatedAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (context.HttpContext.User.Identity.IsAuthenticated)
                context.Result = new RedirectToActionResult(nameof(AccountController.Index), "Account", null);
        }
    }
}



[AllowAnonymous, IsAuthenticated]
public IActionResult Index()
{
    return View();
}
mortenma71
źródło
0

możesz odziedziczyć kontroler, a następnie użyć go w filtrze akcji

wewnątrz klasy ActionFilterAttribute:

   if( filterContext.Controller is MyController )
      if(filterContext.HttpContext.Session["login"] == null)
           (filterContext.Controller as MyController).RedirectToAction("Login");

wewnątrz kontrolera podstawowego:

public class MyController : Controller 
{
    public void  RedirectToAction(string actionName) { 
        base.RedirectToAction(actionName); 
    }
}

Cons. ma to na celu zmianę wszystkich kontrolerów na dziedziczenie z klasy „MyController”

Muhammad Soliman
źródło