Respondí esta pregunta: Cómo asegurar una API web ASP.NET hace 4 años usando HMAC.
Ahora, muchas cosas cambiaron en seguridad, especialmente JWT se está volviendo popular. Aquí trataré de explicar cómo usar JWT de la manera más simple y básica que pueda, para que no nos perdamos de la selva de OWIN, Oauth2, ASP.NET Identity ... :).
Si no conoce el token JWT, debe echar un vistazo a:
https://tools.ietf.org/html/rfc7519
Básicamente, un token JWT se ve así:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Ejemplo:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
Un token JWT tiene tres secciones:
- Encabezado: formato JSON que está codificado en Base64
- Reclamaciones: formato JSON que está codificado en Base64.
- Firma: Creado y firmado basado en Encabezado y Reclamaciones que está codificado en Base64.
Si usa el sitio web jwt.io con el token anterior, puede decodificar el token y verlo como a continuación:
Técnicamente, JWT utiliza una firma que está firmada desde encabezados y notificaciones con algoritmo de seguridad especificado en los encabezados (ejemplo: HMACSHA256). Por lo tanto, se requiere que JWT se transfiera a través de HTTP si almacena información confidencial en las notificaciones.
Ahora, para usar la autenticación JWT, realmente no necesita un middleware OWIN si tiene un sistema Web Api heredado. El concepto simple es cómo proporcionar el token JWT y cómo validar el token cuando llega la solicitud. Eso es.
De vuelta a la demostración, para mantener el token JWT ligero, solo almaceno username
y expiration time
en JWT. Pero de esta manera, debe reconstruir una nueva identidad local (principal) para agregar más información como: roles ... si desea hacer la autorización de roles. Pero, si desea agregar más información a JWT, depende de usted: es muy flexible.
En lugar de usar el middleware OWIN, simplemente puede proporcionar un punto final de token JWT mediante la acción del controlador:
public class TokenController : ApiController
{
// This is naive endpoint for demo, it should use Basic authentication
// to provide token or POST request
[AllowAnonymous]
public string Get(string username, string password)
{
if (CheckUser(username, password))
{
return JwtManager.GenerateToken(username);
}
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
public bool CheckUser(string username, string password)
{
// should check in the database
return true;
}
}
Esta es una acción ingenua; en producción, debe usar una solicitud POST o un punto final de autenticación básica para proporcionar el token JWT.
¿Cómo generar el token basado en username
?
Puede usar el paquete NuGet llamado System.IdentityModel.Tokens.Jwt
desde Microsoft para generar el token, o incluso otro paquete si lo desea. En la demostración, uso HMACSHA256
con SymmetricKey
:
/// <summary>
/// Use the below code to generate symmetric Secret Key
/// var hmac = new HMACSHA256();
/// var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 20)
{
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
El punto final para proporcionar el token JWT está hecho. Ahora, ¿cómo validar el JWT cuando llega la solicitud? En la demostración que he creado,
JwtAuthenticationAttribute
que hereda de IAuthenticationFilter
(más detalles sobre el filtro de autenticación aquí ).
Con este atributo, puede autenticar cualquier acción: solo tiene que poner este atributo en esa acción.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
También puede usar el middleware OWIN o DelegateHander si desea validar todas las solicitudes entrantes para su WebAPI (no específico para el Controlador o la acción)
A continuación se muestra el método principal del filtro de autenticación:
private static bool ValidateToken(string token, out string username)
{
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null)
return false;
if (!identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
username = usernameClaim?.Value;
if (string.IsNullOrEmpty(username))
return false;
// More validate to check whether username exists in system
return true;
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
// based on username to get more information from database
// in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
El flujo de trabajo es usar la biblioteca JWT (paquete NuGet arriba) para validar el token JWT y luego regresar ClaimsPrincipal
. Puede realizar más validaciones, como verificar si el usuario existe en su sistema y agregar otras validaciones personalizadas si lo desea. El código para validar el token JWT y recuperar el principal:
public static ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(Secret);
var validationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal;
}
catch (Exception)
{
//should write log
return null;
}
}
Si se valida el token JWT y se devuelve el principal, debe crear una nueva identidad local y agregar más información para verificar la autorización de roles.
Recuerde agregar config.Filters.Add(new AuthorizeAttribute());
(autorización predeterminada) en el ámbito global para evitar cualquier solicitud anónima a sus recursos.
Puede usar Postman para probar la demostración:
Solicitar token (ingenuo como mencioné anteriormente, solo para demostración):
GET http://localhost:{port}/api/token?username=cuong&password=1
Coloque el token JWT en el encabezado de la solicitud autorizada, por ejemplo:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
La demostración se incluye aquí: https://github.com/cuongle/WebApi.Jwt
hmac = new HMACSHA256();var key = Convert.ToBase64String(hmac.Key);
Logré lograrlo con un mínimo esfuerzo (tan simple como con ASP.NET Core).
Para eso utilizo el
Startup.cs
archivo OWIN y laMicrosoft.Owin.Security.Jwt
biblioteca.Para que la aplicación llegue
Startup.cs
, necesitamos modificarWeb.config
:Así es como
Startup.cs
debe verse:Muchos de ustedes usan ASP.NET Core hoy en día, por lo que, como pueden ver, no difiere mucho de lo que tenemos allí.
Realmente me dejó perplejo primero, estaba tratando de implementar proveedores personalizados, etc. Pero no esperaba que fuera tan simple.
OWIN
solo rocas!Solo una cosa para mencionar: después de habilitar la
NSWag
biblioteca de inicio OWIN , dejó de funcionar para mí (por ejemplo, algunos de ustedes querrían generar automáticamente proxy HTTP de mecanografía para la aplicación Angular).La solución también era muy simple - Me reemplazado
NSWag
conSwashbuckle
y no tenía ninguna otra cuestión.Ok, ahora compartiendo
ConfigHelper
código:Otro aspecto importante: envié el token JWT a través del encabezado de autorización , por lo que el código mecanografiado me busca de la siguiente manera:
(El siguiente código es generado por NSWag )
Ver encabezados parte -
"Authorization": "Bearer " + localStorage.getItem('token')
fuente
I replaced NSWag with Swashbuckle and didn't have any further issues.
¿Swashbuckle tiene la capacidad de generar archivos de mecanografía o es algo que usted mismo agregó?Aquí hay una implementación mínima y segura de una autenticación basada en notificaciones utilizando el token JWT en una API web ASP.NET Core.
En primer lugar, debe exponer un punto final que devuelve un token JWT con notificaciones asignadas a un usuario:
ahora necesita Agregar autenticación a sus servicios
ConfigureServices
dentro de su startup.cs para agregar autenticación JWT como su servicio de autenticación predeterminado como este:ahora puede agregar políticas a sus servicios de autorización como esta:
ALTERNATIVAMENTE , también puede (no es necesario) llenar todos sus reclamos de su base de datos, ya que esto solo se ejecutará una vez en el inicio de su aplicación y agregarlos a políticas como esta:
ahora puede poner el filtro de Políticas en cualquiera de los métodos que desea autorizar de esta manera:
Espero que esto ayude
fuente
Creo que debería usar algún servidor de fiesta 3D para admitir el token JWT y no hay compatibilidad JWT lista para usar en WEB API 2.
Sin embargo, hay un proyecto OWIN para admitir algún formato de token firmado (no JWT). Funciona como un protocolo OAuth reducido para proporcionar solo una forma simple de autenticación para un sitio web.
Puede leer más sobre esto, por ejemplo, aquí .
Es bastante largo, pero la mayoría de las partes son detalles con controladores e identidad ASP.NET que puede que no necesite en absoluto. Lo más importante son
Allí puede leer cómo configurar un punto final (por ejemplo, "/ token") al que puede acceder desde la interfaz (y detalles sobre el formato de la solicitud).
Otros pasos proporcionan detalles sobre cómo conectar ese punto final a la base de datos, etc. y puede elegir las partes que necesita.
fuente
En mi caso, el JWT es creado por una API separada, por lo que ASP.NET solo necesita decodificarlo y validarlo. A diferencia de la respuesta aceptada, estamos utilizando RSA, que es un algoritmo no simétrico, por lo que la
SymmetricSecurityKey
clase mencionada anteriormente no funcionará.Aquí está el resultado.
fuente