c# - example - jwt net core 2
JWT y API web(JwtAuthForWebAPI?)-Buscando un ejemplo (2)
Hice la implementación simple de JwtAuthForWebAPI y funciona :)
El token JWT no es aceptado por la autenticación Web APi2 con JwtAuthForWebAPI
Tengo un proyecto de API web encabezado por Angular, y quiero asegurarlo usando un token JWT. Ya he tenido la validación de usuario / paso, por lo que creo que solo necesito implementar la parte JWT.
Creo que me decidí por JwtAuthForWebAPI, por lo que un ejemplo de uso sería genial.
Supongo que cualquier método no decorado con [Autorizar] se comportará como siempre lo hace, y que cualquier método decorado con [Autorizar] se 401 si el token pasado por el cliente no coincide.
Lo que aún no puedo entender es cómo enviar el token al cliente después de la autenticación inicial.
Estoy tratando de usar una cadena mágica para comenzar, así que tengo este código:
RegisterRoutes(GlobalConfiguration.Configuration.Routes);
var builder = new SecurityTokenBuilder();
var jwtHandler = new JwtAuthenticationMessageHandler
{
AllowedAudience = "http://xxxx.com",
Issuer = "corp",
SigningToken = builder.CreateFromKey(Convert.ToBase64String(new byte[]{4,2,2,6}))
};
GlobalConfiguration.Configuration.MessageHandlers.Add(jwtHandler);
Pero no estoy seguro de cómo eso regresa al cliente inicialmente. Creo que entiendo cómo manejar esto en el cliente, pero puntos adicionales si también puede mostrar el lado angular de esta interacción.
Terminé teniendo que tomar información de varios lugares diferentes para crear una solución que me funcione (en realidad, el comienzo de una solución de producción viable, ¡pero funciona!)
Me deshice de JwtAuthForWebAPI (aunque tomé prestada una parte de él para permitir que las solicitudes sin encabezado de Autorización fluyeran a los métodos del Controlador WebAPI no protegidos por [Autorizar]).
En su lugar, estoy usando la biblioteca JWT de Microsoft ( JSON Web Token Handler para Microsoft .NET Framework - de NuGet).
En mi método de autenticación, después de realizar la autenticación real, creo la versión de cadena del token y la devuelvo junto con el nombre autenticado (el mismo nombre de usuario que me fue asignado, en este caso) y un rol que, en realidad, probablemente se derivará durante la autenticación.
Aquí está el método:
[HttpPost]
public LoginResult PostSignIn([FromBody] Credentials credentials)
{
var auth = new LoginResult() { Authenticated = false };
if (TryLogon(credentials.UserName, credentials.Password))
{
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, credentials.UserName),
new Claim(ClaimTypes.Role, "Admin")
}),
AppliesToAddress = ConfigurationManager.AppSettings["JwtAllowedAudience"],
TokenIssuerName = ConfigurationManager.AppSettings["JwtValidIssuer"],
SigningCredentials = new SigningCredentials(new
InMemorySymmetricSecurityKey(JwtTokenValidationHandler.SymmetricKey),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256")
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
auth.Token = tokenString;
auth.Authenticated = true;
}
return auth;
}
ACTUALIZAR
Hubo una pregunta acerca del manejo del token en solicitudes posteriores. Lo que hice fue crear un DelegatingHandler para tratar de leer / decodificar el token, luego crear un Principal y configurarlo en Thread.CurrentPrincipal y HttpContext.Current.User (es necesario configurarlo en ambos). Finalmente, decoro los métodos del controlador con las restricciones de acceso apropiadas.
Aquí está la carne del DelegatingHandler:
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
return false;
}
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
var authHeader = request.Headers.Authorization;
if (authHeader == null)
{
// missing authorization header
return base.SendAsync(request, cancellationToken);
}
if (!TryRetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
try
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters =
new TokenValidationParameters()
{
AllowedAudience = ConfigurationManager.AppSettings["JwtAllowedAudience"],
ValidIssuer = ConfigurationManager.AppSettings["JwtValidIssuer"],
SigningToken = new BinarySecretSecurityToken(SymmetricKey)
};
IPrincipal principal = tokenHandler.ValidateToken(token, validationParameters);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
No olvide agregarlo en la canalización de MessageHandlers:
public static void Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new JwtTokenValidationHandler());
}
Finalmente, decora tus métodos de control:
[Authorize(Roles = "OneRoleHere")]
[GET("/api/admin/settings/product/allorgs")]
[HttpGet]
public List<Org> GetAllOrganizations()
{
return QueryableDependencies.GetMergedOrganizations().ToList();
}
[Authorize(Roles = "ADifferentRoleHere")]
[GET("/api/admin/settings/product/allorgswithapproval")]
[HttpGet]
public List<ApprovableOrg> GetAllOrganizationsWithApproval()
{
return QueryableDependencies.GetMergedOrganizationsWithApproval().ToList();
}