Implementera HTTP-autentisering i Web API

I den här artikeln skulle jag presentera en diskussion om implementering av HTTP-autentisering i Web API. Det finns två sätt på vilka du kan implementera HTTP-autentisering i din Web Api. Dessa inkluderar:

  • Formulärautentisering
  • Grundläggande autentisering

Vi skulle inte betrakta Windows-autentisering som en genomförbar strategi eftersom du inte kan exponera din tjänst via Internet om du använder Windows-autentisering.

Säkra Web Api med hjälp av formulärautentisering

Formulärverifiering använder ASP.Net-medlemsleverantören och använder HTTP-standardkakor istället för behörighetsrubriken. Formulärverifiering är inte så REST-vänlig eftersom den använder kakor, och klienterna skulle behöva hantera kakor för att konsumera tjänster som utnyttjar formulärautentisering, vilket är sårbart för förfalskningsattacker på plats. Det är därför du behöver implementera CSRF-åtgärder om du använder formulärautentisering. Formulärverifiering använder inte kryptering för att säkra användarens referenser. Därför är detta inte en säker strategi om du inte kör ditt webb-API över SSL.

Säker webb-API med grundläggande autentisering

Grundläggande autentisering skickar användarens referenser i klagetext över kabeln. Om du skulle använda grundläggande autentisering bör du använda ditt webb-API över ett Secure Socket Layer (SSL). När vi använder grundläggande autentisering skickar vi användarens referenser eller autentiseringstoken i rubriken på HTTP-begäran. Tjänsten på serversidan skulle behöva analysera rubriken för att hämta autentiseringstoken. Om begäran inte är en giltig begäran returnerar servern HTTP 401, vilket betyder ett obehörigt svar.

Låt oss utforska hur vi kan utföra grundläggande autentisering med hjälp av ett åtgärdsfilter. För att göra detta bör du skapa en klass som härleder System.Web.Http.Filters.ActionFilterAttributeklassen enligt nedan:

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        private Boolean IsUserValid(Dictionary credentials)

        {

            if (credentials["UserName"].Equals("joydip") && credentials["Password"].Equals("joydip123"))

                return true;

            return false;

        }

         private Dictionary ParseRequestHeaders(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            Dictionary credentials = new Dictionary();

             var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

            httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

             string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

            string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

            string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

             credentials.Add("UserName", username);

            credentials.Add("Password", password);

             return credentials;

        }

         public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                     Dictionary credentials = ParseRequestHeaders(actionContext);

                     if (IsUserValid(credentials))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                 }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage

(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

Vi kontrollerar om behörighetshuvudet finns; om inte, returneras ett HTTP 401- eller "obehörigt" svar.

Nästa steg är att validera användaruppgifterna som skickas via rubriken för behörighetsbegäran från klienten. Innan vi gör det bör vi veta hur webb-API ska kallas från klienten. För detta har jag förberett en testmetod. Testmetoden använder HttpClientklassen för att anropa webb-API. Observera att användarnamnen konverteras till Base64-strängformat innan de skickas. Testmetoden ges nedan.

[TestMethod]

        public void BasicAuthenticationTest()

        {

            string username = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip"));

            string password = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip123"));

            HttpClient client = new HttpClient();

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", username + ":" + password);

            var result = client.GetAsync(new Uri("//localhost//api/default/")).Result;

           Assert.IsTrue(result.IsSuccessStatusCode);

        }

Som du kan se i ovanstående kodavsnitt skickas användaruppgifterna med auktoriseringshuvudet.

Nu när klienten är redo, låt oss slutföra implementeringen av BasicAuthenicationFilterklassen. Inuti OnActionExecutingmetoden skulle vi behöva analysera rubrikvärdet i den här klassen och kontrollera om referensuppgifterna från klienten matchar. För nu, låt oss anta att användarnamnet och lösenordet har värden för joydipoch joydip123respektive (de är hårdkodade). Här är den fullständiga koden för BasicAuthenticationFilterklassen som innehåller validering av användaruppgifterna.

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

                    var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

                    httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

                    string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

                    string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

                    string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

                    if (username.Equals("joydip") && password.Equals("joydip123"))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

I din kontrollerklass bör du ange attributet på lämpligt sätt. Observera att BasicAuthenticationattributet här hänvisar till den BasicAuthenticationAttributeklass vi implementerade.

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable Get()

        {

            return new string[] { "Joydip", "Kanjilal" };

        }

    }

Nu, lite konfiguration --- du måste konfigurera attributet så att samtal till din controller skulle filtreras på lämpligt sätt för att autentiseringen ska fungera.

 public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(

                name: "DefaultApi",

                routeTemplate: "api/{controller}/{id}",

                defaults: new { id = RouteParameter.Optional }

            );

            config.Formatters.Remove(config.Formatters.XmlFormatter);

            GlobalConfiguration.Configuration.Filters.Add(new BasicAuthenticationAttribute());

        }

    }

Och du är klar! När du utför testfallet klarar testet.

Du bör ändå se till att autentiseringsuppgifterna inte är hårdkodade. snarare bör de lagras i en databas och du bör hämta dem och validera i klassens OnActionExecutingmetod BasicAuthenticationAttribute.