c# - por - web api ejemplo
Autenticación basada en tokens en API web sin ninguna interfaz de usuario (2)
Estoy desarrollando una API REST en la API web ASP.Net. Solo se podrá acceder a mi API a través de clientes que no estén basados en un navegador. Necesito implementar seguridad para mi API, así que decidí usar la autenticación basada en tokens. Tengo una buena comprensión de la autenticación basada en tokens y he leído algunos tutoriales, pero todos tienen alguna interfaz de usuario para iniciar sesión. No necesito ninguna interfaz de usuario para iniciar sesión, ya que el cliente pasará los detalles de inicio de sesión a través de HTTP POST, que será autorizado desde nuestra base de datos. ¿Cómo puedo implementar la autenticación basada en token en mi API? Tenga en cuenta que se accederá a mi API en alta frecuencia, así que también tengo que ocuparme del rendimiento. Por favor, avíseme si puedo explicarlo mejor.
Creo que hay cierta confusión sobre la diferencia entre MVC y Web Api. En resumen, para MVC puede usar un formulario de inicio de sesión y crear una sesión usando cookies. Para Web Api no hay sesión. Por eso quieres usar el token.
No necesita un formulario de inicio de sesión. El punto final del token es todo lo que necesitas. Como Win describió, enviará las credenciales al punto final del token donde se maneja.
Aquí hay un código C # del lado del cliente para obtener un 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;
}
}
Para usar el token, agréguelo al encabezado de la solicitud:
//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;
}
}
Donde Token es:
//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; }
}
Ahora para el lado del servidor:
En 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);
Y en ApplicationOAuthProvider.cs el código que realmente otorga o niega el acceso:
//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);
}
}
Como puede ver, no hay ningún controlador involucrado en la recuperación del token. De hecho, puede eliminar todas las referencias MVC si solo desea una API web. He simplificado el código del lado del servidor para hacerlo más legible. Puede agregar código para actualizar la seguridad.
Asegúrese de usar solo SSL. Implemente el RequireHttpsAttribute para forzar esto.
Puede usar los atributos Authorize / AllowAnonymous para proteger su API web. Además, puede agregar filtros (como RequireHttpsAttribute) para hacer que su Web Api sea más segura. Espero que esto ayude.
La API web de ASP.Net ya tiene incorporado el servidor de autorización. Puede verlo dentro de Startup.cs cuando crea una nueva aplicación web ASP.Net con plantilla de API web.
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
};
Todo lo que tiene que hacer es publicar un nombre de usuario y contraseña codificados con URL dentro de la cadena de consulta.
/Token/userName=johndoe%40example.com&password=1234&grant_type=password
Si desea conocer más detalles, puede ver Registro de usuario e inicio de sesión: Angular de adelante hacia atrás con API web de Deborah Kurata .