Uwierzytelnianie oparte na tokenach w Web API bez żadnego interfejsu użytkownika

Rozwijam REST API w ASP.Net Web API. Moje API będzie dostępne tylko za pośrednictwem klientów spoza przeglądarki. Muszę zaimplementować zabezpieczenia dla mojego API, więc zdecydowałem się na uwierzytelnianie Tokenowe. Dobrze rozumiem uwierzytelnianie oparte na tokenach i przeczytałem kilka samouczków, ale wszystkie mają interfejs użytkownika do logowania. Nie potrzebuję żadnego interfejsu użytkownika do logowania, ponieważ dane logowania będą przekazywane przez Klienta przez HTTP POST, który zostanie autoryzowany z naszej bazy danych. Jak można Implementuję uwierzytelnianie oparte na tokenach w moim API? Uwaga - moje API będzie dostępne z dużą częstotliwością, więc muszę również zadbać o wydajność. Proszę dać mi znać, jeśli Mogę to lepiej wyjaśnić.

Author: Legolas Greenleaf, 2016-07-29

2 answers

Myślę, że jest pewne zamieszanie co do różnicy między MVC a Web Api. W skrócie, dla MVC możesz użyć formularza logowania i utworzyć sesję za pomocą Plików cookie. Dla Web Api nie ma sesji. Dlatego chcesz użyć żetonu.

Nie potrzebujesz formularza logowania. Token endpoint jest wszystkim, czego potrzebujesz. Podobnie jak w opisie Win, wyślesz poświadczenia do punktu końcowego tokena, w którym są obsługiwane.

Oto kod C# po stronie klienta, aby uzyskać token:

    //using System;
    //using System.Collections.Generic;
    //using System.Net;
    //using System.Net.Http;
    //string token = GetToken("https://localhost:<port>/", userName, password);

    static string GetToken(string url, string userName, string password) {
        var pairs = new List<KeyValuePair<string, string>>
                    {
                        new KeyValuePair<string, string>( "grant_type", "password" ), 
                        new KeyValuePair<string, string>( "username", userName ), 
                        new KeyValuePair<string, string> ( "Password", password )
                    };
        var content = new FormUrlEncodedContent(pairs);
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        using (var client = new HttpClient()) {
            var response = client.PostAsync(url + "Token", content).Result;
            return response.Content.ReadAsStringAsync().Result;
        }
    }

W celu wykorzystania token dodaje go do nagłówka żądania:

    //using System;
    //using System.Collections.Generic;
    //using System.Net;
    //using System.Net.Http;
    //var result = CallApi("https://localhost:<port>/something", token);

    static string CallApi(string url, string token) {
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        using (var client = new HttpClient()) {
            if (!string.IsNullOrWhiteSpace(token)) {
                var t = JsonConvert.DeserializeObject<Token>(token);

                client.DefaultRequestHeaders.Clear();
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token);
            }
            var response = client.GetAsync(url).Result;
            return response.Content.ReadAsStringAsync().Result;
        }
    }

Gdzie Token to:

//using Newtonsoft.Json;

class Token
{
    public string access_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
    public string userName { get; set; }
    [JsonProperty(".issued")]
    public string issued { get; set; }
    [JsonProperty(".expires")]
    public string expires { get; set; }
}

Teraz po stronie serwera:

W Startupie.Auth.cs

        var oAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider("self"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // https
            AllowInsecureHttp = false
        };
        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(oAuthOptions);

Oraz w Aplikacjiauthprovider.cs kod, który faktycznie udziela lub odmawia dostępu:

//using Microsoft.AspNet.Identity.Owin;
//using Microsoft.Owin.Security;
//using Microsoft.Owin.Security.OAuth;
//using System;
//using System.Collections.Generic;
//using System.Security.Claims;
//using System.Threading.Tasks;

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private readonly string _publicClientId;

    public ApplicationOAuthProvider(string publicClientId)
    {
        if (publicClientId == null)
            throw new ArgumentNullException("publicClientId");

        _publicClientId = publicClientId;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

        var user = await userManager.FindAsync(context.UserName, context.Password);
        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager);
        var propertyDictionary = new Dictionary<string, string> { { "userName", user.UserName } };
        var properties = new AuthenticationProperties(propertyDictionary);

        AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
        // Token is validated.
        context.Validated(ticket);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }
        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
            context.Validated();

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (context.ClientId == _publicClientId)
        {
            var expectedRootUri = new Uri(context.Request.Uri, "/");

            if (expectedRootUri.AbsoluteUri == context.RedirectUri)
                context.Validated();
        }
        return Task.FromResult<object>(null);
    }

}

Jak widać, nie ma kontrolera zaangażowanego w pobieranie tokena. W rzeczywistości możesz usunąć wszystkie odwołania do MVC, jeśli chcesz mieć tylko Web Api. Uprościłem kod po stronie serwera, aby był bardziej czytelny. Możesz dodać kod do / align = "left" /

Upewnij się, że używasz tylko SSL. Zaimplementuj RequireHttpsAttribute, aby wymusić to.

Możesz użyć atrybutów Authorize / AllowAnonymous, aby zabezpieczyć swoje API internetowe. Dodatkowo możesz dodać filtry (takie jak RequireHttpsAttribute), aby Twoje API internetowe było bezpieczniejsze. Mam nadzieję, że to pomoże.

 57
Author: Ruard van Elburg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-30 04:01:57

ASP.Net Web API ma już wbudowany serwer autoryzacji. możesz zobaczyć go wewnątrz Startup.cs podczas tworzenia nowego ASP.Net aplikacja webowa z szablonem Web API.

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true
};

Wszystko, co musisz zrobić, to zamieścić adres URL zakodowanej nazwy użytkownika i hasła wewnątrz ciągu zapytania.

/Token/userName=johndoe%40example.com&password=1234&grant_type=password

Jeśli chcesz poznać więcej szczegółów, możesz obejrzeć Rejestracja i logowanie użytkowników-od przodu do tyłu za pomocą Web API by Deborah Kurata .

 15
Author: Win,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 14:49:35