Odpowiedziałem na to pytanie: Jak zabezpieczyć interfejs API sieci Web ASP.NET 4 lata temu za pomocą HMAC.
Teraz wiele rzeczy zmieniło się pod względem bezpieczeństwa, szczególnie JWT zyskuje na popularności. W tym miejscu postaram się wyjaśnić, jak korzystać z JWT w najprostszy i podstawowy sposób, w jaki mogę, abyśmy nie zgubili się w dżungli OWIN, Oauth2, ASP.NET Identity ... :).
Jeśli nie znasz tokena JWT, musisz rzucić okiem na:
https://tools.ietf.org/html/rfc7519
Zasadniczo token JWT wygląda następująco:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Przykład:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
Token JWT ma trzy sekcje:
- Nagłówek: format JSON zakodowany w Base64
- Roszczenia: Format JSON zakodowany w Base64.
- Podpis: utworzony i podpisany na podstawie nagłówka i oświadczeń zakodowanych w Base64.
Jeśli używasz witryny jwt.io z powyższym tokenem, możesz go zdekodować i zobaczyć jak poniżej:
Technicznie JWT używa podpisu podpisanego z nagłówków i oświadczeń za pomocą algorytmu bezpieczeństwa określonego w nagłówkach (przykład: HMACSHA256). Dlatego wymagane jest przesyłanie JWT przez HTTPs, jeśli przechowujesz poufne informacje w roszczeniach.
Teraz, aby korzystać z uwierzytelniania JWT, tak naprawdę nie potrzebujesz oprogramowania pośredniego OWIN, jeśli masz starszy system Web Api. Prosta koncepcja polega na tym, jak dostarczyć token JWT i jak sprawdzić poprawność tokena, gdy nadejdzie żądanie. Otóż to.
Powrót na demo, aby zachować JWT tokena lekka, tylko przechowywanie username
i expiration time
w JWT. Ale w ten sposób musisz odbudować nową tożsamość lokalną (podmiot główny), aby dodać więcej informacji, takich jak: role .. jeśli chcesz wykonać autoryzację roli. Ale jeśli chcesz dodać więcej informacji do JWT, to zależy od ciebie: jest bardzo elastyczny.
Zamiast używać oprogramowania pośredniego OWIN, możesz po prostu podać punkt końcowy tokena 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;
}
}
To naiwne działanie; w środowisku produkcyjnym należy użyć żądania POST lub punktu końcowego podstawowego uwierzytelniania, aby dostarczyć token JWT.
Jak wygenerować token na podstawie username
?
Możesz użyć pakietu NuGet wywoływanego System.IdentityModel.Tokens.Jwt
od Microsoft do wygenerowania tokena, a nawet innego pakietu, jeśli chcesz. W wersji 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 do dostarczenia tokenu JWT został zakończony. Jak sprawdzić poprawność JWT, gdy nadejdzie żądanie? W wersji demonstracyjnej, którą zbudowałem,
JwtAuthenticationAttribute
która dziedziczy IAuthenticationFilter
(więcej szczegółów na temat filtra uwierzytelniania tutaj ).
Za pomocą tego atrybutu możesz uwierzytelnić dowolne działanie: wystarczy umieścić ten atrybut na tym działaniu.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
Możesz również użyć oprogramowania pośredniego OWIN lub DelegateHander, jeśli chcesz zweryfikować wszystkie przychodzące żądania dotyczące interfejsu WebAPI (nie dotyczy kontrolera ani działania)
Poniżej znajduje się podstawowa metoda z filtru 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 sprawdzenia poprawności tokena JWT, a następnie powrotu ClaimsPrincipal
. Możesz przeprowadzić większą weryfikację, np. Sprawdzić, czy użytkownik istnieje w systemie i dodać inne niestandardowe weryfikacje, jeśli chcesz. Kod sprawdzania poprawności tokenu JWT i odzyskania kwoty głównej:
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 jest sprawdzony, a nazwa główna jest zwracana, należy 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 zakresie globalnym, aby zapobiec anonimowym żądaniom do Twoich zasobów.
Możesz użyć Listonosza do przetestowania wersji demo:
Żeton żądania (naiwny, jak wspomniałem powyżej, tylko dla wersji demonstracyjnej):
GET http://localhost:{port}/api/token?username=cuong&password=1
Umieść token JWT w nagłówku autoryzowanego żądania, przykład:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
Demo znajduje się tutaj: https://github.com/cuongle/WebApi.Jwt
hmac = new HMACSHA256();var key = Convert.ToBase64String(hmac.Key);
Udało mi się to osiągnąć przy minimalnym wysiłku (tak prostym jak w przypadku ASP.NET Core).
Do tego używam
Startup.cs
pliku iMicrosoft.Owin.Security.Jwt
biblioteki OWIN .Aby aplikacja mogła trafić
Startup.cs
, musimy zmienićWeb.config
:Oto jak
Startup.cs
powinien wyglądać:Wielu z was korzysta obecnie z platformy ASP.NET Core, więc jak widać, nie różni się ona zbytnio od tego, co mamy.
Najpierw naprawdę mnie to zakłopotało, próbowałem wdrożyć niestandardowych dostawców itp. Ale nie spodziewałem się, że będzie to takie proste.
OWIN
tylko skały!Jeszcze jedna rzecz do wspomnienia - po włączeniu OWIN Startup
NSWag
biblioteka przestała dla mnie działać (np. Niektórzy z was mogą chcieć automatycznie generować serwery proxy maszynopisu HTTP dla aplikacji Angular).Rozwiązanie było również bardzo proste - zastąpiłem
NSWag
jeSwashbuckle
i nie miałem żadnych dalszych problemów.Ok, teraz udostępniam
ConfigHelper
kod:Kolejny ważny aspekt - wysłałem Token JWT przez nagłówek autoryzacji , więc kod maszynowy szuka mnie w następujący sposób:
(poniższy kod jest generowany przez NSWag )
Zobacz część nagłówków -
"Authorization": "Bearer " + localStorage.getItem('token')
źródło
I replaced NSWag with Swashbuckle and didn't have any further issues.
Czy Swashbuckle ma możliwość generowania plików maszynopisu, czy jest to coś, co sam do niego dodałeś?Oto bardzo minimalna i bezpieczna implementacja uwierzytelniania opartego na oświadczeniach przy użyciu tokenu JWT w interfejsie API sieci Web platformy ASP.NET Core.
po pierwsze musisz ujawnić punkt końcowy, który zwraca token JWT z oświadczeniami przypisanymi do użytkownika:
Teraz trzeba dodać do swoich usług uwierzytelniania w
ConfigureServices
wewnątrz startup.cs dodać uwierzytelniania JWT jako domyślną usługę uwierzytelniania tak:teraz możesz dodać zasady do swoich usług autoryzacyjnych w następujący sposób:
ALTERNATYWNIE Możesz również (niekoniecznie) wypełnić wszystkie swoje roszczenia z bazy danych, ponieważ uruchomi się to tylko raz podczas uruchamiania aplikacji i doda je do zasad takich jak to:
teraz możesz ustawić filtr zasad na dowolną z metod, które chcesz autoryzować w następujący sposób:
Mam nadzieję że to pomoże
źródło
Myślę, że powinieneś użyć serwera 3d party do obsługi tokena JWT i nie ma gotowej obsługi JWT w WEB API 2.
Istnieje jednak projekt OWIN do obsługi pewnego formatu podpisanego tokena (nie JWT). Działa jako zredukowany protokół OAuth, zapewniając prostą formę uwierzytelnienia dla strony internetowej.
Możesz przeczytać więcej na ten temat np . Tutaj .
Jest dość długi, ale większość części to szczegóły dotyczące kontrolerów i tożsamości ASP.NET, które mogą być w ogóle niepotrzebne. Najważniejsze są
Tam możesz przeczytać, jak skonfigurować punkt końcowy (np. „/ Token”), do którego masz dostęp z poziomu interfejsu użytkownika (i szczegółowe informacje na temat formatu żądania).
Inne kroki zawierają szczegółowe informacje na temat łączenia tego punktu końcowego z bazą danych itp., A także możesz wybrać potrzebne części.
źródło
W moim przypadku JWT jest tworzony przez oddzielny interfejs API, więc ASP.NET musi tylko go zdekodować i sprawdzić. W przeciwieństwie do przyjętej odpowiedzi używamy RSA, który jest algorytmem niesymetrycznym, więc
SymmetricSecurityKey
wspomniana wyżej klasa nie będzie działać.Oto wynik.
źródło