MVC 4 Web API Custom Authorize Attribut


Wie bereits in meinem letzten Artikel erwähnt, benötigt man für Web API ein eigenes Authorize Attribut aus einem anderen .NET Namespace. Das hier verwendete Authorize Attribut kommt aus dem Namespace “System.Web.Http.AuthorizeAttribute”.

Auch hier bietet Microsoft wieder eine Standardlösung an, die nur prüft ob der User eingeloggt ist, dafür muss wie bisher gewohnt im Web API Controller das Authorize Attribut aus dem oben genannten Namespace verwendet werden.

Gibt man mit seinen Web API Aufrufen aber Nutzerspezifische Daten zurück, die nicht jeder eingeloggte User abrufen darf, wird hier wieder ein eigenes Authorize Attribut benötigt. Hier handelt es sich um den gleichen Aufbau wie beim normalen Authorize Attribut. Man kann wieder auf den Controler, Action, Id und sonstige Routing Informationen zugreifen und dann entsprechend im Custom Authorize Attribut darauf reagieren. Damit man weiß welcher User eingeloggt ist muss auch hier wieder das “FormsAuthentication.SetAuthCookie” beim normalen Seitenlogin benutzt werden.

Ich möchte zusätzlich noch darauf hinweisen, das ich für meine Web API Aufrufe nicht die Standardroute verwende. Damit wir die Web API auch so nutzen können wie wir es bisher von den MVC Controllern gewohnt sind, müssen wir noch eine weitere Route zu unserer WebApi hinzufügen. Denn mit der Standardroute für API aufrufe sind nur GET/POST/DELETE Abfragen möglich. Aber meist hat man mehr wie nur diese “paar” Abfragen die man für einen View benötigt, daher habe ich noch eine neue Route vor der “DefaultApi” Route hinzugefügt.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //Registrieren unseres Filters für die Modelvalidation für AJAX Requests
        config.Filters.Add(new ValidateAttribute());

        //Alternatives Routing, hier ist es jetzt wieder möglich gezielte Actionen und Controller abzufragen
        //In der DefaultApi kann man nur Get/Post/Delete abfragen und ist damit sehr begrenzt was die Abfragen angeht.
        config.Routes.MapHttpRoute(
            name: "AlternateWebApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        //Standardroute  - hier lässt sich nur GET/POST/DELETE abfragen.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Unser Benutzerdefiniertes Web API Authorize Attribut sieht dann folgendermaßen aus und hier darauf achten das man das richtige Authorize Attribut aus dem richtigen Namespace einbindet!

using AuthorizeAttribute = System.Web.Http.AuthorizeAttribute;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomWebApiAuthorizeAttribute : AuthorizeAttribute
{
    #region Member
    private HttpActionContext CurrentContext { get; set; }
    #endregion

    /// <summary>
    /// Prüfen ob der User auch Autorisiert ist, auf die passenden API Aufrufe zuzugreifen
    /// </summary>
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        //Wenn der User nicht eingeloggt ist, dann wird er hier schon mit einem 401 abgewiesen
        base.OnAuthorization(actionContext);

        //Der Filtercontext wird benötigt um auf die RequestDaten zuzugreifen, z.b. auf die UserId die zugegriffen
        CurrentContext = actionContext;
       

        //Wenn kein User eingeloggt ist, dann hat er auch keinen Zugriff.
        if (!HttpContext.Current.User.Identity.IsAuthenticated)
        {
            //Wenn der User nicht eingeloggt ist, auf die Loginseite verweisen
            HandleUnauthorizedRequest(actionContext);
        }
        else
        {
            //Prüfen der passenden Rechte für die einzelnen Actions/Methoden
            //Wenn der User nicht für den View/Action authentifiziert ist, dann auf die Loginseite verweisen.
            if (!CheckRights())
            {
                HandleUnauthorizedRequest(actionContext);
            }
        }

        //Wenn alles i.o. ist "nichts" unternehmen und einfach beim Aufbau der Seite weitermachen.
    }

    /// <summary>
    /// Den unautorisierten Eingriff, "abwehren"
    /// </summary>
    /// <param name="actionContext"></param>
    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        throw new HttpResponseException(challengeMessage);
    }

    #region Private Functions
    /// <summary>
    /// Prüfen um welchen CurrentController es sich handelt und die passende Sicherheitsprüfung vornehmen
    /// Hier werden Nur API Controller überprüft
    /// </summary>
    /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
    private bool CheckRights()
    {
        //Setzen des aktuellen Actionnamen, der aufgerufen wird.
        string actionName = CurrentContext.ActionDescriptor.ActionName;
        //Laden des Controllernamens über den der API Aufruf kam
        string controllerName = CurrentContext.ControllerContext.ControllerDescriptor.ControllerName;
        //Auslesen der ID, wenn eine in der URL mit übergeben wurde.
        string id = CurrentContext.ControllerContext.RouteData.Values.ContainsKey("id") ?
                    CurrentContext.ControllerContext.RouteData.Values["id"].ToString() : null;

        //Den aktuellen User ermitteln der den API Aufruf gestartet hat.
        string username = HttpContext.Current.User.Identity.Name;

        //TODO Prüfung vornehmen ob der Aktuelle User auf die ermittelten Resourcen zugreifen darf.

        return true;
    }
    #endregion
}

Das Attribut kann dann wieder problemlos über dem passenden ApiController verwendet werden

[CustomWebApiAuthorize]
public class WorktimeApiController : ApiController
{
    /// <summary>
    /// Filtert unsere Arbeitszeiteinträge nach den letzten Einträgen die vorgenommen wurde.
    /// </summary>
    /// <param name="id">Die zugehörige UserId zu der die Einträge gefiltert werden sollen</param>
    /// <param name="value">Unsere Modeldaten nach denen gefiltert werden soll</param>
    public HttpResponseMessage ByEntryFilter(long id, WorktimeEntryFilterModel value)
    {
        if (value != null)
        {
            WorktimeListModel worktime = new WorktimeListModel(id, value);
            //Den passen Listentyp speichern, wenn der jeweilige Filter ausgewählt wurde.
            worktime.SaveDefaultListType(id, EWorktimeListTypes.ByEntries);
            worktime.FilterWorktimeList();

            //Wird automatisch in JSON Objekt umgewandelt und gibt den StatusCode 200 für OK zurück!
            HttpResponseMessage response = Request.CreateResponse<WorktimeListModel>(HttpStatusCode.OK, worktime);
            return response;
        }

        //Statuscode 400 Zurückgeben
        throw new HttpResponseException(HttpStatusCode.BadRequest);
    }

...
Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s