Implementirajte HTTP provjeru autentičnosti u web API

U ovom članku predstavit ću raspravu o implementaciji HTTP provjere autentičnosti u Web API. Postoje dva načina na koja možete implementirati HTTP provjeru autentičnosti u svoj Web Api. To uključuje:

  • Obrasci provjeru autentičnosti
  • Osnovna provjera autentičnosti

Autentifikaciju sustava Windows ne bismo smatrali izvedivom strategijom jer ne možete izložiti svoju uslugu putem Interneta ako koristite autentifikaciju sustava Windows.

Osiguravanje Web Api pomoću autentifikacije obrazaca

Autentifikacija obrazaca koristi dobavljača članstva ASP.Net i koristi standardne HTTP kolačiće umjesto zaglavlja Autorizacija. Autentifikacija obrazaca nije REST-prikladna jer koristi kolačiće, a klijenti bi trebali upravljati kolačićima kako bi koristili usluge koje koriste autentifikaciju obrazaca, što je ranjivo na napade krivotvorenja na više web lokacija. Zbog toga biste trebali primijeniti mjere CSRF-a ako koristite provjeru autentičnosti obrazaca. Autentifikacija obrazaca ne koristi šifriranje za osiguravanje vjerodajnica korisnika. Stoga ovo nije sigurna strategija ako svoj web API ne pokrenete preko SSL-a.

Sigurni web API pomoću osnovne provjere autentičnosti

Osnovna autentifikacija šalje vjerodajnice korisnika u običnom tekstu preko žice. Ako biste koristili osnovnu provjeru autentičnosti, trebali biste koristiti svoj web API preko sloja sigurne utičnice (SSL). Kada koristimo osnovnu provjeru autentičnosti, prosljeđivali bismo vjerodajnice korisnika ili token provjere autentičnosti u zaglavlju HTTP zahtjeva. Usluga na strani poslužitelja trebala bi raščlaniti zaglavlje da bi dohvatila token za provjeru autentičnosti. Ako zahtjev nije valjan, poslužitelj vraća HTTP 401, što znači neovlašteni odgovor.

Istražimo kako možemo izvršiti osnovnu provjeru autentičnosti pomoću akcijskog filtra. Da biste to učinili, trebali biste stvoriti klasu koja izvodi System.Web.Http.Filters.ActionFilterAttributeklasu kao što je prikazano u nastavku:

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

            }

        }

    }

Provjeravamo je li prisutno zaglavlje autorizacije; ako nije, vraća se HTTP 401 ili "neovlašteni" odgovor.

Sljedeći je korak provjera valjanosti korisničkih vjerodajnica koje je klijent proslijedio putem zaglavlja zahtjeva za autorizaciju. Prije nego što to učinimo, trebali bismo znati kako web API treba pozvati od klijenta. Za ovo sam pripremio test metodu. Test metoda koristi HttpClientklasu za pozivanje web API-ja. Imajte na umu da se korisnička imena pretvaraju u format niza Base64 prije nego što se proslijede. Metoda ispitivanja data je u nastavku.

[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);

        }

Kao što možete vidjeti u gornjem isječku koda, vjerodajnice korisnika prosljeđuju se pomoću zaglavlja autorizacije.

Sad kad je klijent spreman, dovršimo implementaciju BasicAuthenicationFilterklase. Unutar OnActionExecutingmetode trebali bismo raščlaniti vrijednost zaglavlja u ovoj klasi i provjeriti podudaraju li se vjerodajnice isporučene s klijenta. Za sada pretpostavimo da korisničko ime i lozinka imaju vrijednosti joydipi joydip123, odnosno (teško su kodirani). Evo kompletnog koda BasicAuthenticationFilterklase koji uključuje provjeru valjanosti korisničkih vjerodajnica.

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

            }

        }

    }

U klasi kontrolera trebali biste navesti atribut na odgovarajući način. Imajte na umu da se BasicAuthenticationovdje atribut odnosi na BasicAuthenticationAttributeklasu koju smo implementirali.

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable Get()

        {

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

        }

    }

Sada, malo konfiguracije - morate konfigurirati atribut tako da se pozivi vašem kontroleru filtriraju na odgovarajući način da bi provjera autentičnosti mogla funkcionirati.

 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());

        }

    }

I gotovi ste! Kada izvršite testni slučaj, test prolazi.

Svejedno biste trebali osigurati da vjerodajnice nisu čvrsto kodirane; radije ih treba pohraniti u bazu podataka, a vi biste ih trebali dohvatiti i potvrditi u OnActionExecutingmetodi BasicAuthenticationAttributeklase.