Respondi a esta pergunta: Como proteger uma API da Web do ASP.NET há 4 anos usando o HMAC.
Agora, muitas coisas mudaram em segurança, especialmente o JWT está ficando popular. Aqui, tentarei explicar como usar o JWT da maneira mais simples e básica possível, para que não nos percam da selva de OWIN, Oauth2, ASP.NET Identity ... :).
Se você não conhece o token JWT, precisa dar uma olhada em:
https://tools.ietf.org/html/rfc7519
Basicamente, um token JWT se parece com:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Exemplo:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
Um token JWT possui três seções:
- Cabeçalho: formato JSON codificado em Base64
- Reivindicações: formato JSON codificado em Base64.
- Assinatura: criada e assinada com base no cabeçalho e nas reivindicações, codificado em Base64.
Se você usar o site jwt.io com o token acima, poderá decodificar o token e vê-lo como abaixo:
Tecnicamente, o JWT usa assinatura assinada a partir de cabeçalhos e declarações com o algoritmo de segurança especificado nos cabeçalhos (exemplo: HMACSHA256). Portanto, é necessário que o JWT seja transferido por HTTPs se você armazenar informações confidenciais nas declarações.
Agora, para usar a autenticação JWT, você realmente não precisa de um middleware OWIN se tiver um sistema Web Api herdado. O conceito simples é como fornecer o token JWT e como validar o token quando a solicitação chegar. É isso aí.
De volta à demonstração, para manter o token JWT leve, eu apenas armazeno username
eexpiration time
no JWT. Porém, dessa maneira, é necessário recriar a nova identidade local (principal) para adicionar mais informações como: functions .. se você deseja autorizar a função. Mas, se você deseja adicionar mais informações ao JWT, você decide: é muito flexível.
Em vez de usar o middleware OWIN, você pode simplesmente fornecer um ponto de extremidade do token JWT usando a ação do 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 é uma ação ingênua; na produção, você deve usar uma solicitação POST ou um terminal de autenticação básica para fornecer o token JWT.
Como gerar o token com base username
?
Você pode usar o pacote NuGet chamado System.IdentityModel.Tokens.Jwt
da Microsoft para gerar o token ou até outro pacote, se desejar. Na demonstração, eu uso HMACSHA256
com 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;
}
O terminal para fornecer o token JWT está pronto. Agora, como validar o JWT quando a solicitação chega? Na demonstração que construí,
JwtAuthenticationAttribute
que herda de IAuthenticationFilter
(mais detalhes sobre o filtro de autenticação aqui ).
Com esse atributo, você pode autenticar qualquer ação: basta colocar esse atributo nessa ação.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
Você também pode usar o middleware OWIN ou o DelegateHander se desejar validar todas as solicitações recebidas pela sua WebAPI (não específica ao Controller ou ação)
Abaixo está o método principal do filtro de autenticação:
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);
}
O fluxo de trabalho é, usando a biblioteca JWT (pacote NuGet acima) para validar o token JWT e depois retornar ClaimsPrincipal
. Você pode executar mais validações, como verificar se o usuário existe no seu sistema e adicionar outras validações personalizadas, se desejar. O código para validar o token JWT e recuperar o 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;
}
}
Se o token JWT for validado e o principal for retornado, você deverá construir uma nova identidade local e colocar mais informações para verificar a autorização da função.
Lembre-se de adicionar config.Filters.Add(new AuthorizeAttribute());
(autorização padrão) no escopo global para impedir qualquer solicitação anônima aos seus recursos.
Você pode usar o Postman para testar a demonstração:
Token de solicitação (ingênuo como mencionei acima, apenas para demonstração):
GET http://localhost:{port}/api/token?username=cuong&password=1
Coloque o token JWT no cabeçalho da solicitação autorizada, exemplo:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
A demonstração é apresentada aqui: https://github.com/cuongle/WebApi.Jwt
hmac = new HMACSHA256();var key = Convert.ToBase64String(hmac.Key);
Consegui consegui-lo com o mínimo esforço (tão simples quanto no ASP.NET Core).
Para isso, uso o
Startup.cs
arquivo e aMicrosoft.Owin.Security.Jwt
biblioteca OWIN .Para que o aplicativo seja exibido
Startup.cs
, precisamos alterarWeb.config
:Veja como
Startup.cs
deve ficar:Muitos de vocês usam o ASP.NET Core atualmente, portanto, como você pode ver, não difere muito do que temos lá.
Isso realmente me deixou perplexo primeiro, eu estava tentando implementar provedores personalizados etc. Mas não esperava que fosse tão simples.
OWIN
apenas pedras!Apenas uma coisa a mencionar - depois que eu habilitei a
NSWag
biblioteca OWIN Startup , parei de funcionar para mim (por exemplo, alguns de vocês podem querer gerar automaticamente proxies HTTP datilografados para aplicativo Angular).A solução também foi muito simples - I substituído
NSWag
comSwashbuckle
e não ter quaisquer outras questões.Ok, agora compartilhando
ConfigHelper
código:Outro aspecto importante - enviei o JWT Token pelo cabeçalho de autorização , para que o código de texto digitado me procure da seguinte maneira:
(o código abaixo é gerado pelo NSWag )
Veja a parte dos cabeçalhos -
"Authorization": "Bearer " + localStorage.getItem('token')
fonte
I replaced NSWag with Swashbuckle and didn't have any further issues.
O Swashbuckle tem a capacidade de gerar arquivos datilografados ou é algo que você adicionou a ele mesmo?Aqui está uma implementação muito mínima e segura de uma autenticação baseada em declarações usando o token JWT em uma API da Web do ASP.NET Core.
Antes de tudo, você precisa expor um terminal que retorna um token JWT com declarações atribuídas a um usuário:
agora você precisa adicionar autenticação aos seus serviços
ConfigureServices
dentro do seu startup.cs para adicionar a autenticação JWT como seu serviço de autenticação padrão como este:agora você pode adicionar políticas aos seus serviços de autorização como este:
ALTERNATIVAMENTE , você também pode (não necessário) preencher todas as suas reivindicações do banco de dados, pois isso só será executado uma vez na inicialização do aplicativo e as adicionará a políticas como esta:
agora você pode colocar o filtro de política em qualquer um dos métodos que você deseja autorizar assim:
Espero que isto ajude
fonte
Eu acho que você deve usar algum servidor de festa em 3D para suportar o token JWT e não há suporte JWT pronto para uso na API WEB 2.
No entanto, há um projeto OWIN para oferecer suporte a algum formato de token assinado (não JWT). Funciona como um protocolo OAuth reduzido para fornecer apenas uma forma simples de autenticação para um site.
Você pode ler mais sobre isso, por exemplo, aqui .
É bastante longo, mas a maioria das partes são detalhes com controladores e identidade do ASP.NET que você pode não precisar. Os mais importantes são
Lá, você pode ler como configurar o ponto de extremidade (por exemplo, "/ token") que você pode acessar do front-end (e detalhes sobre o formato da solicitação).
Outras etapas fornecem detalhes sobre como conectar esse terminal ao banco de dados, etc., e você pode escolher as partes necessárias.
fonte
No meu caso, o JWT é criado por uma API separada, portanto o ASP.NET precisa apenas decodificá-lo e validá-lo. Ao contrário da resposta aceita, estamos usando o RSA, que é um algoritmo não simétrico, portanto a
SymmetricSecurityKey
classe mencionada acima não funcionará.Aqui está o resultado.
fonte