JWT i Web API (JwtAuthForWebAPI?)- Szukając Przykładu
Mam projekt Web API prowadzony przez Angular i chcę go zabezpieczyć za pomocą tokena JWT. Mam już walidację user / pass, więc myślę, że po prostu muszę zaimplementować część JWT.
Wydaje mi się, że zdecydowałem się na JwtAuthForWebAPI, więc przykład z tego byłby świetny.
Zakładam, że każda metoda nie ozdobiona [Authorize] będzie zachowywać się tak, jak zawsze, i że każda metoda ozdobiona [Authorize] będzie 401, jeśli token przekazany przez Klienta nie mecz.
Czego jeszcze nie wiem, jak wysłać token z powrotem do klienta po początkowym uwierzytelnieniu.
Próbuję użyć magicznego ciągu, aby rozpocząć, więc mam ten kod:
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);
Ale nie jestem pewien, jak to wróci do klienta. Myślę, że rozumiem, jak poradzić sobie z tym na kliencie, ale punkty bonusowe, jeśli możesz również pokazać kątową stronę tej interakcji. 2 answers
Skończyło się na tym, że musiałem wziąć informacje z kilku różnych miejsc, aby stworzyć rozwiązanie, które działa dla mnie (w rzeczywistości, początki produkcji realne rozwiązanie-ale to działa!)
Pozbyłem się JwtAuthForWebAPI (chociaż pożyczyłem z niego jeden kawałek, aby umożliwić przepływ żądań bez nagłówka autoryzacji do metod kontrolera WebAPI, które nie są strzeżone przez [Authorize]).
Zamiast tego używam Biblioteki JWT Microsoftu (JSON Web Token Handler dla Microsoft. NET Framework - od NuGet).
W mojej metodzie uwierzytelniania, po wykonaniu rzeczywistego uwierzytelniania, tworzę wersję łańcuchową tokena i przekazuję ją z powrotem wraz z uwierzytelnioną nazwą (w tym przypadku ta sama nazwa użytkownika została mi przekazana) i rolą, która w rzeczywistości prawdopodobnie zostanie zaczerpnięta podczas uwierzytelniania.
Oto metoda:
[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;
}
UPDATE
Pojawiło się pytanie o obsługę tokena przy kolejnych żądaniach. Stworzyłem DelegatingHandler, aby spróbować odczytać / zdekodować token, a następnie utworzyć Principal i ustawić go w wątku.CurrentPrincipal i HttpContext.Aktualne.User (musisz go ustawić w obu). Na koniec udekoruję metody kontrolera odpowiednimi ograniczeniami dostępu.
Oto mięso delegata:
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));
}
Nie zapomnij dodać go do rurociągu MessageHandlers:
public static void Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new JwtTokenValidationHandler());
}
Na koniec udekoruj swoje metody kontrolera:
[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();
}
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
2014-09-17 20:25:11
Zrobiłem jwtauthforwebapi prostą implementację i działa:)
Token JWT nie jest akceptowany przez uwierzytelnianie Web API2 za pomocą JwtAuthForWebAPI
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-05-23 12:18:07