Zaimplementuj uwierzytelnianie HTTP w interfejsie API sieci Web

W tym artykule przedstawię dyskusję na temat implementacji uwierzytelniania HTTP w Web API. Istnieją dwa sposoby zaimplementowania uwierzytelniania HTTP w interfejsie Web Api. Obejmują one:

  • Uwierzytelnianie formularzy
  • Uwierzytelnianie podstawowe

Nie uważalibyśmy uwierzytelniania systemu Windows za wykonalną strategię, ponieważ nie można udostępniać usługi przez Internet, jeśli korzystasz z uwierzytelniania systemu Windows.

Zabezpieczanie interfejsu API sieci Web przy użyciu uwierzytelniania za pomocą formularzy

Uwierzytelnianie formularzy korzysta z dostawcy członkostwa ASP.Net i używa standardowych plików cookie HTTP zamiast nagłówka Authorization. Uwierzytelnianie za pomocą formularzy nie jest tak przyjazne dla REST, ponieważ wykorzystuje pliki cookie, a klienci musieliby zarządzać plikami cookie, aby korzystać z usług korzystających z uwierzytelniania formularzy, co jest podatne na ataki polegające na fałszowaniu między lokacjami. Dlatego też, jeśli używasz uwierzytelniania formularzy, musisz zaimplementować środki CSRF. Uwierzytelnianie za pomocą formularzy nie używa szyfrowania do zabezpieczenia poświadczeń użytkownika. Dlatego nie jest to bezpieczna strategia, chyba że używasz internetowego interfejsu API przez SSL.

Bezpieczny interfejs API sieci Web przy użyciu podstawowego uwierzytelniania

Uwierzytelnianie podstawowe przesyła przez sieć poświadczenia użytkownika w postaci zwykłego tekstu. Jeśli miałbyś używać podstawowego uwierzytelniania, powinieneś używać swojego internetowego interfejsu API za pośrednictwem protokołu Secure Socket Layer (SSL). Korzystając z uwierzytelniania podstawowego, przekazaliśmy dane uwierzytelniające użytkownika lub token uwierzytelniający w nagłówku żądania HTTP. Usługa po stronie serwera musiałaby przeanalizować nagłówek, aby pobrać token uwierzytelniania. Jeśli żądanie nie jest poprawnym żądaniem, serwer zwraca HTTP 401, co oznacza nieautoryzowaną odpowiedź.

Przyjrzyjmy się, jak możemy przeprowadzić podstawowe uwierzytelnianie za pomocą filtru akcji. Aby to zrobić, należy utworzyć klasę pochodną System.Web.Http.Filters.ActionFilterAttributeklasy, jak pokazano poniżej:

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

            }

        }

    }

Sprawdzamy, czy występuje nagłówek autoryzacji; jeśli nie, zwracana jest odpowiedź HTTP 401 lub „nieautoryzowana”.

Następnym krokiem jest sprawdzenie poświadczeń użytkownika przekazanych przez nagłówek żądania autoryzacji od klienta. Zanim to zrobimy, powinniśmy wiedzieć, jak wywołać Web API z klienta. W tym celu przygotowałem metodę testową. Metoda testowa używa HttpClientklasy do wywołania internetowego interfejsu API. Zwróć uwagę, że nazwy użytkowników są konwertowane na format łańcucha Base64 przed ich przekazaniem. Metodę badania podano poniżej.

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

        }

Jak widać na powyższym fragmencie kodu, poświadczenia użytkownika są przekazywane za pomocą nagłówka autoryzacji.

Teraz, gdy klient jest gotowy, zakończmy implementację tej BasicAuthenicationFilterklasy. Wewnątrz OnActionExecutingmetody musielibyśmy przeanalizować wartość nagłówka w tej klasie i sprawdzić, czy poświadczenia podane przez klienta są zgodne. Na razie załóżmy, że nazwa użytkownika i hasło mają odpowiednio wartości joydipi joydip123(są zakodowane na stałe). Oto pełny kod BasicAuthenticationFilterklasy, który obejmuje walidację poświadczeń użytkownika.

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

            }

        }

    }

W swojej klasie kontrolera powinieneś odpowiednio określić atrybut. Zwróć uwagę, że BasicAuthenticationatrybut tutaj odnosi się do BasicAuthenticationAttributeklasy, którą zaimplementowaliśmy.

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable Get()

        {

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

        }

    }

Teraz trochę konfiguracji - musisz skonfigurować atrybut tak, aby wywołania twojego kontrolera były odpowiednio filtrowane, aby uwierzytelnianie działało.

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

        }

    }

Gotowe! Po wykonaniu przypadku testowego test przechodzi pomyślnie.

Mimo to powinieneś upewnić się, że poświadczenia nie są zakodowane na stałe; powinny być raczej przechowywane w bazie danych i powinieneś je pobrać i zweryfikować w OnActionExecutingmetodzie BasicAuthenticationAttributeklasy.