Autenticação JWT para Asp.Net Api Web

Estou a tentar apoiar o JWT bearer token (JSON Web Token) na minha aplicação web api e estou a perder-me.

vejo suporte para .net core e para OWIN aplicações.
Estou neste momento a apresentar a minha candidatura no IIS.

Como posso obter este módulo de autenticação na minha aplicação? Existe alguma forma de eu poder usar a configuração <authentication> semelhante à forma como eu uso a autenticação\windows do formulário?

Author: Cuong Le, 2016-10-27

3 answers

Eu respondi a esta pergunta: ASP.NET API Web 4 anos atrás usando HMAC. Muitas coisas mudaram na segurança, a esp JWT está a ficar popular. Aqui, vou tentar explicar como usar o JWT da maneira mais simples e básica que puder, para não nos perdermos da selva de OWIN, Oauth2, ASP.NET identidade... :). Se não conheces o JWT token, tens de dar uma vista de olhos. at:

Https://tools.ietf.org/html/rfc7519

Basicamente, um símbolo JWT parece:

<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

Exemplo:

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXvcj9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmjmijoxndc3nty1nzi0lcjlehaioje0nzc1njy5mjqsimlhdci6mtq3nzu2ntcynh0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjdyhm9zysdfq

O símbolo JWT tem três secções:

  1. cabeçalho: formato JSON que é codificado como um base64
  2. reivindicações: formato JSON que está codificado como base64.
  3. assinatura: criada e assinada com base no cabeçalho e reivindicações codificadas como base64.

Se utilizar o sítio web jwt.io com o símbolo acima, você pode descodificar e ver o símbolo como abaixo:

enter image description here

Tecnicamente, o JWT usa a assinatura que é assinada pelos cabeçalhos e reivindicações com o algoritmo de segurança especificado nos cabeçalhos (exemplo: HMACSHA256). Por conseguinte, a JWT deve ser transferida através dos HTTPs se você armazenar qualquer informação sensível em reivindicações.

Agora, para usar a autenticação JWT, você realmente não precisa de um middleware OWIN se você tem um sistema de Api Web legado. O conceito simples é como fornecer token JWT e como validar token quando o pedido vem. É isso.

De volta para a demo, para manter o token JWT leve, só guardo username e expiration time em JWT. Mas desta forma, você tem que refazer a nova identidade local (principal) para adicionar mais informações como: papéis.. se quero fazer a autorização do papel. Mas, se quiser adicionar mais informação ao JWT, é consigo, muito flexível.

Em vez de usar o middleware OWIN, pode simplesmente fornecer o ponto final do token JWT usando a acçã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 acção ingênua, na produção deve usar o POST request ou o Basic Authentication endpoint para fornecer o JWT token.

Como gerar o token baseado em username?

Pode utilizar o pacote NuGet chamado System.IdentityModel.Tokens.Jwt de MS para gerar o token, ou mesmo outro pacote, se quiser. Na demo, 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 objectivo para fornecer o token JWT está feito, agora, como validar o JWT quando o pedido chegar, na demo que eu construí JwtAuthenticationAttribute que herda de IAuthenticationFilter, mais detalhes sobre o filtro de autenticação em aqui .

Com este atributo, você pode autenticar qualquer acção, basta colocar este atributo nessa acção.

public class ValueController : ApiController
{
    [JwtAuthentication]
    public string Get()
    {
        return "value";
    }
}

Também pode usar o OWIN middleware ou DelegateHander se quiser validar todos os pedidos recebidos para a sua WebApi (não específico no controlador ou acçã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 item JWT e depois voltar ClaimsPrincipal. Você pode realizar mais validação, como verificar se o usuário existe no seu sistema e adicionar outras validações personalizadas, se quiser. O código para validar o token JWT e obter o principal verso:

   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 devolvido, você deve construir uma nova identidade local e colocar mais informações nele para verificar a autorização do papel.

Lembre-se de adicionar config.Filters.Add(new AuthorizeAttribute()); (autorização por omissão) no âmbito global, a fim de evitar qualquer pedido anónimo aos seus recursos.

Pode usar o carteiro para testar a demonstração:

Peça token (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 para pedido autorizado, exemplo:

GET http://localhost:{port}/api/value

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
A demo está aqui. https://github.com/cuongle/WebApi.Jwt
 378
Author: Cuong Le, 2017-11-29 21:22:38

Eu acho que você deve usar algum servidor de festa 3d para suportar o token JWT e não há nenhum suporte fora da caixa JWT na API 2 WEB.

No entanto, existe um projecto OWIN para apoiar algum formato de Símbolo assinado (não JWT). Ele funciona como um protocolo OAuth reduzido para fornecer apenas uma forma simples de autenticação para um site web.

Pode ler mais sobre ele, por exemplo aqui .

É bastante longo,mas a maioria das peças são detalhes com controladores e ... ASP.NET identidade que podes não precisar de nada. Os mais importantes são

Passo 9: Adicionar suporte para a geração de Tokens ao portador de OAuth

Passo 12: testar a API traseira

Aí você pode ler como configurar o endpoint (por exemplo, "/token") que você pode acessar a partir do frontend (e detalhes sobre o formato do pedido).

Outras etapas fornecem detalhes sobre como ligar esse parâmetro à base de dados, etc. e podes escolher as partes de que precisas.

 1
Author: Ilya Chernomordik, 2016-10-27 09:47:46

Eu também implementei a API do Jason Web Token no meu projecto, você pode baixar a partir deste link JWT API Token . Pode utilizar [authorize] para verificar se um Utilizador está autenticado ou não?

 1
Author: Brijesh Mavani, 2018-06-13 05:46:41