Implementa l'autenticazione HTTP nell'API Web

In questo articolo presenterò una discussione sull'implementazione dell'autenticazione HTTP nell'API Web. Esistono due modi in cui è possibile implementare l'autenticazione HTTP nella propria API Web. Questi includono:

  • Autenticazione basata su moduli
  • Autenticazione di base

Non considereremo l'autenticazione di Windows come una strategia fattibile poiché non puoi esporre il tuo servizio su Internet se utilizzi l'autenticazione di Windows.

Protezione delle API Web utilizzando l'autenticazione basata su moduli

L'autenticazione basata su form utilizza il provider di appartenenze ASP.Net e utilizza i cookie HTTP standard invece dell'intestazione di autorizzazione. L'autenticazione basata su moduli non è adatta a REST in quanto utilizza i cookie ei client dovrebbero gestire i cookie per utilizzare servizi che sfruttano l'autenticazione basata su moduli, vulnerabile agli attacchi di falsificazione tra siti. Questo è il motivo per cui dovresti implementare misure CSRF se utilizzi l'autenticazione basata su form. L'autenticazione basata su moduli non utilizza la crittografia per proteggere le credenziali dell'utente. Quindi, questa non è una strategia sicura a meno che non esegui la tua API Web su SSL.

Secure Web API utilizzando l'autenticazione di base

L'autenticazione di base invia le credenziali dell'utente nel testo del reclamo via cavo. Se dovessi utilizzare l'autenticazione di base, dovresti utilizzare la tua API Web su un Secure Socket Layer (SSL). Quando si utilizza l'autenticazione di base, si passano le credenziali dell'utente o il token di autenticazione nell'intestazione della richiesta HTTP. Il servizio sul lato server dovrebbe analizzare l'intestazione per recuperare il token di autenticazione. Se la richiesta non è una richiesta valida, il server restituisce HTTP 401, ovvero una risposta non autorizzata.

Esploriamo come possiamo eseguire l'autenticazione di base utilizzando un filtro di azione. Per fare ciò, dovresti creare una classe che derivi la System.Web.Http.Filters.ActionFilterAttributeclasse come mostrato di seguito:

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

            }

        }

    }

Controlliamo se è presente l'intestazione dell'autorizzazione; in caso contrario, viene restituita una risposta HTTP 401 o "non autorizzata".

Il passaggio successivo consiste nel convalidare le credenziali utente passate tramite l'intestazione della richiesta di autorizzazione dal client. Prima di farlo, dovremmo sapere come deve essere chiamata l'API Web dal client. Per questo, ho preparato un metodo di prova. Il metodo di test utilizza la HttpClientclasse per chiamare l'API Web. Si noti che i nomi utente vengono convertiti nel formato stringa Base64 prima di essere passati. Il metodo di prova è fornito di seguito.

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

        }

Come puoi vedere nello snippet di codice sopra, le credenziali dell'utente vengono passate utilizzando l'intestazione dell'autorizzazione.

Ora che il client è pronto, completiamo l'implementazione della BasicAuthenicationFilterclasse. All'interno del OnActionExecutingmetodo avremmo bisogno di analizzare il valore dell'intestazione in questa classe e controllare se le credenziali fornite dal client corrispondono. Per ora, supponiamo che il nome utente e la password abbiano rispettivamente i valori joydipe joydip123(sono hardcoded). Ecco il codice completo della BasicAuthenticationFilterclasse che incorpora la validazione delle credenziali utente.

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

            }

        }

    }

Nella classe del controller è necessario specificare l'attributo in modo appropriato. Nota che l' BasicAuthenticationattributo qui si riferisce alla BasicAuthenticationAttributeclasse che abbiamo implementato.

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable Get()

        {

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

        }

    }

Ora, un po 'di configurazione --- è necessario configurare l'attributo in modo che le chiamate al controller vengano filtrate in modo appropriato affinché l'autenticazione funzioni.

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

        }

    }

E hai finito! Quando si esegue il test case, il test viene superato.

Dovresti comunque assicurarti che le credenziali non siano hard-coded; piuttosto, dovrebbero essere memorizzati in un database e dovresti recuperarli e convalidarli nel OnActionExecutingmetodo della BasicAuthenticationAttributeclasse.