it-swarm.com.de

Token-basierte Authentifizierung in der Web-API ohne Benutzeroberfläche

Ich entwickle eine REST API in der ASP.Net-Web-API. Auf meine API kann nur über nicht browserbasierte Clients zugegriffen werden. Ich muss die Sicherheit für meine API implementieren, daher habe ich mich für Token entschieden Ich verstehe die tokenbasierte Authentifizierung ziemlich gut und habe einige Tutorials gelesen, aber alle haben eine Benutzeroberfläche für die Anmeldung. Ich benötige keine Benutzeroberfläche für die Anmeldung, da die Anmeldedaten vom Client über HTTP weitergegeben werden POST die aus unserer Datenbank autorisiert werden. Wie kann ich eine tokenbasierte Authentifizierung in meiner API implementieren? Bitte beachten Sie, dass auf meine API mit hoher Häufigkeit zugegriffen wird, sodass ich mich auch um die Leistung kümmern muss. Bitte lassen Sie es mich wissen, wenn ich es besser erklären kann.

61
Souvik Ghosh

Ich denke, es gibt einige Verwirrung über den Unterschied zwischen MVC und Web-API. Kurz gesagt, für MVC können Sie ein Anmeldeformular verwenden und mithilfe von Cookies eine Sitzung erstellen. Für Web-API gibt es keine Sitzung. Deshalb möchten Sie das Token verwenden.

Sie benötigen kein Anmeldeformular. Der Token-Endpunkt ist alles, was Sie brauchen. Wie von Win beschrieben, senden Sie die Anmeldeinformationen an den Token-Endpunkt, auf dem sie verarbeitet werden.

Hier ist ein clientseitiger C # -Code, um ein Token zu erhalten:

    //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;
        }
    }

Um das Token zu verwenden, fügen Sie es dem Header der Anfrage hinzu:

    //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;
        }
    }

Wo Token ist:

//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; }
}

Nun zur Serverseite:

In Startup.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);

Und in ApplicationOAuthProvider.cs der Code, der den Zugriff tatsächlich gewährt oder verweigert:

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

}

Wie Sie sehen, ist kein Controller am Abrufen des Tokens beteiligt. Tatsächlich können Sie alle MVC-Referenzen entfernen, wenn Sie nur eine Web-API möchten. Ich habe den serverseitigen Code vereinfacht, um ihn besser lesbar zu machen. Sie können Code hinzufügen, um die Sicherheit zu verbessern.

Stellen Sie sicher, dass Sie nur SSL verwenden. Implementieren Sie das RequireHttpsAttribute, um dies zu erzwingen.

Sie können die Authorize/AllowAnonymous-Attribute verwenden, um Ihre Web-API zu sichern. Zusätzlich können Sie Filter (wie RequireHttpsAttribute) hinzufügen, um Ihre Web-API sicherer zu machen. Ich hoffe das hilft.

76

In der ASP.Net-Web-API ist Authorization Server bereits integriert. Sie können es in Startup.cs sehen, wenn Sie eine neue ASP.Net-Webanwendung mit einer Web-API-Vorlage erstellen.

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

Sie müssen lediglich den URL-codierten Benutzernamen und das Kennwort in die Abfragezeichenfolge eingeben.

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

Wenn Sie mehr Details wissen möchten, können Sie Benutzerregistrierung und Login - Angular Front to Back mit Web API von Deborah Kurata .

19
Win