Uwierzytelnianie JWT dla Asp.Net Web Api

Próbuję obsługiwać JWT bearer token (JSON Web Token) w mojej aplikacji web api i gubię się.

Widzę wsparcie dla .net core i dla OWIN aplikacji.
Obecnie hostuję moją aplikację na IIS.

Jak mogę osiągnąć ten moduł uwierzytelniania w mojej aplikacji? Czy jest jakiś sposób na użycie konfiguracji <authentication> podobnej do sposobu, w jaki używam uwierzytelniania\windows form?

Author: Cuong Le, 2016-10-27

3 answers

Odpowiedziałem na to pytanie: Jak zabezpieczyć ASP.NET Web API 4 years ago using HMAC.

Teraz, wiele rzeczy zmieniło się w bezpieczeństwie, esp JWT staje się popularny. W tym miejscu postaram się wyjaśnić jak używać JWT w najprostszy i podstawowy sposób jaki mogę, żeby nie zgubić się z jungle of OWIN, Oauth2, ASP.NET tożsamość... :).

Jeśli nie znasz JWT token, musisz rzucić okiem trochę at:

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

W zasadzie token JWT wygląda tak:

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

Przykład:

EyJhbGciOiJIUzI1NiIsInR5cCI6IkpXvcj9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmjmijoxndc3nty1nzi0lcjlehaioje0nzc1njy5mjqsimlhdci6mtq3nzu2ntcynh0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjdyhm9zysdfq

JWT token ma trzy sekcje:

  1. nagłówek: format JSON, który jest zakodowany jako base64
  2. twierdzenie: format JSON który jest zakodowany jako base64.
  3. Signature: utworzone i podpisane na podstawie nagłówka i oświadczeń, które są zakodowane jako base64.

Jeśli korzystasz ze strony internetowej jwt.io z tokenem powyżej, możesz odszyfrować i zobaczyć token jak poniżej:

Tutaj wpisz opis obrazka

Technicznie JWT używa podpisu, który jest podpisywany z nagłówków i oświadczeń za pomocą algorytmu bezpieczeństwa określonego w nagłówkach (przykład: HMACSHA256). Dlatego JWT musi być przesyłane przez HTTPs jeśli przechowujesz poufne informacje w roszczeniach.

Teraz, aby używać uwierzytelniania JWT, tak naprawdę nie potrzebujesz oprogramowania pośredniczącego OWIN, jeśli masz starszy system Web Api. Prosta koncepcja polega na tym, jak dostarczyć token JWT i jak zweryfikować token, gdy nadejdzie żądanie. To wszystko.

Wracając do dema, aby zachować JWT token lekki, przechowuję tylko username i expiration time w JWT. Ale w ten sposób musisz ponownie zbudować nową tożsamość lokalną (principal), aby dodać więcej informacji, takich jak: role.. jeśli chcesz zrobić autoryzację roli. Ale jeśli chcesz dodać więcej informacji do JWT, to zależy od ciebie, bardzo elastyczny.

Zamiast używać oprogramowania pośredniczącego OWIN, możesz po prostu podać punkt końcowy tokenu JWT, używając akcji z kontrolera:]}
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;
    }
}

Jest to naiwna akcja, w produkcji należy użyć żądania POST lub podstawowego punktu końcowego uwierzytelniania, aby dostarczyć token JWT.

Jak wygenerować token na podstawie username?

Możesz użyć pakietu NuGet o nazwie System.IdentityModel.Tokens.Jwt od MS do Wygeneruj token lub inny pakiet, jeśli chcesz. W demo używam HMACSHA256 z 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;
    }

Punkt końcowy, aby dostarczyć token JWT, jest teraz gotowy, jak zweryfikować JWT, gdy nadejdzie żądanie, w demo, które zbudowałem JwtAuthenticationAttribute który dziedziczy z IAuthenticationFilter, więcej szczegółów o filtrze uwierzytelniania w tutaj .

Za pomocą tego atrybutu możesz uwierzytelnić dowolną akcję, po prostu umieść ten atrybut na tej akcji.

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

Możesz również użyć OWIN oprogramowanie middleware lub DelegateHander, jeśli chcesz zweryfikować wszystkie przychodzące żądania dla swojej WebApi (nie specyficzne dla kontrolera lub akcji)

Poniżej znajduje się podstawowa metoda z filtra uwierzytelniania:

    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);
    }

Przepływ pracy polega na użyciu biblioteki JWT (pakiet NuGet powyżej) do walidacji tokena JWT, a następnie powrotu ClaimsPrincipal. Możesz wykonać więcej walidacji, takich jak sprawdzić, czy użytkownik istnieje w systemie i dodać inne niestandardowe walidacje, jeśli chcesz. Kod do walidacji tokena JWT i uzyskania głównego wstecz:

   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;
        }
    }

Jeśli token JWT zostanie zweryfikowany, a główny zostanie zwrócony, powinieneś zbudować nową tożsamość lokalną i umieścić w niej więcej informacji, aby sprawdzić autoryzację roli.

Pamiętaj, aby dodać config.Filters.Add(new AuthorizeAttribute()); (domyślna autoryzacja) w zasięgu globalnym, aby zapobiec anonimowemu żądaniu do Twoich zasobów.

Możesz użyć listonosza do przetestowania demo:]}

Request token (naiwny, jak wspomniałem powyżej, tylko dla demo):

GET http://localhost:{port}/api/token?username=cuong&password=1

Umieść token JWT w nagłówku dla autoryzowane żądanie, przykład:

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

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s

Demo jest umieszczone tutaj: https://github.com/cuongle/WebApi.Jwt

 378
Author: Cuong Le,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 21:22:38

Myślę, że powinieneś użyć jakiegoś serwera party 3D do obsługi tokena JWT i nie ma out of the box JWT support W WEB API 2.

Istnieje jednak projekt OWIN do obsługi jakiegoś formatu podpisanego tokena (nie JWT). Działa jako zredukowany protokół OAuth, aby zapewnić prostą formę uwierzytelniania dla witryny internetowej.

Więcej na ten temat można przeczytać np. tutaj .

Jest dość długi, ale większość części to detale ze sterownikami i ASP.NET tożsamość, którą ty może w ogóle nie trzeba. Najważniejsze to

Krok 9: dodanie obsługi generowania tokenów na okaziciela OAuth

Krok 12: testowanie Back-endowego API

Tam możesz przeczytać Jak skonfigurować punkt końcowy (np. "/ token"), do którego możesz uzyskać dostęp z frontend (oraz szczegóły dotyczące formatu żądania).

Inne kroki dostarczają informacji o tym, jak połączyć punkt końcowy z bazą danych, itp. możesz wybrać części, których potrzebujesz.

 1
Author: Ilya Chernomordik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-27 09:47:46

Implementuję również Jason Web Token API w moim projekcie, możesz pobrać z tego linku JWT API Token. Możesz użyć [authorize], aby sprawdzić, czy użytkownik jest uwierzytelniony, czy nie?

 1
Author: Brijesh Mavani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 05:46:41