用于Asp.Net Web Api的JWTauthentication

我试图在我的web api应用程序中支持JWT不记名令牌(Json Web令牌),我迷路了。

我看到.net coreOWIN应用程序的支持。
我目前通过IIS托pipe我的应用程序。

我如何在我的应用程序中实现这个authentication模块? 有什么办法可以使用<authentication>configuration类似于我使用窗体\ windows身份validation的方式吗?

我回答了这个问题: 如何在 4年前使用HMAC 来保护ASP.NET Web API 。

现在很多事情都变了,特别是JWT越来越受欢迎。 在这里,我将尝试以最简单和最基本的方式解释如何使用JWT,所以我们不会迷失在OWIN,Oauth2,ASP.NET Identity … :)的丛林中。

如果您不知道JWT令牌,则需要稍微查看一下:

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

基本上,JWT令牌看起来像:

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

例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ

JWT令牌有三个部分:

  1. 标题:被编码为base64的JSON格式
  2. 声明:被编码为base64的JSON格式。
  3. 签名:根据编码为base64的标题和声明创build和签名。

如果您使用带有上述标记的网站jwt.io ,则可以解码并看到如下所示的标记:

在这里输入图像说明

从技术上说,JWT使用从头文件和声明中签名的签名,并在头文件中指定安全algorithm(例如:HMACSHA256)。 因此,如果您将任何敏感信息存储在声明中,则需要通过HTTP传输JWT。

现在,为了使用JWTauthentication,如果您有遗留的Web Api系统,您并不需要OWIN中间件。 简单的概念是如何提供JWT令牌以及在请求到达时如何validation令牌。 而已。

回到演示,为了保持JWT令牌轻量级,我只在JWT中存储usernameexpiration time 。 但是这样,你必须重新build立新的本地身份(主体)来添加更多的信息,如:angular色..如果你想做angular色授权。 但是,如果您想向智威汤逊添加更多信息,这取决于您,非常灵活。

您可以简单地使用来自控制器的操作来提供JWT令牌端点,而不是使用OWIN中间件:

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

这是天真的行动,在生产中,您应该使用POST请求或基本身份validation端点来提供JWT令牌。

如何根据username生成令牌?

你可以使用来自MS的名为System.IdentityModel.Tokens.Jwt的NuGet包来生成令牌,如果你愿意,甚至可以使用另一个包。 在演示中,我使用带有SymmetricKey HMACSHA256

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

提供JWT令牌的端点现在已经完成了,如何在请求发出时validationJWT,在演示中,我已经构build了JwtAuthenticationAttribute ,它inheritance自IAuthenticationFilter ,关于authenticationfilter的更多细节在这里 。

有了这个属性,你可以validation任何行动,你只要把这个属性的行动。

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

你也可以使用OWIN中间件或DelegateHander,如果你想validation所有传入的请求为您的WebApi(不具体在控制器或行动)

以下是来自authenticationfilter的核心方法:

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

工作stream程是使用JWT库(上面的NuGet包)来validationJWT令牌,然后返回ClaimsPrincipal 。 您可以执行更多validation,例如检查系统中是否存在用户,并根据需要添加其他自定义validation。 validationJWT令牌并返回主体的代码:

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

如果JWT令牌得到validation,并且主体是返回的,则应该build立新的本地身份并将更多的信息添加到其中以检查angular色授权。

记得添加config.Filters.Add(new AuthorizeAttribute()); (默认授权)在全球范围内,以防止任何匿名请求到您的资源。

您可以使用邮差testing演示:

请求令牌(天真,如上所述,只是为了演示):

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

将JWT令牌放入授权请求的标题中,例如:

 GET http://localhost:{port}/api/value Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s 

我把我的演示在这里: https : //github.com/cuongle/WebApi.Jwt

我认为你应该使用一些3d派对服务器来支持JWT令牌,并且WEB API 2中没有开箱即用的JWT支持。

但是有一个OWIN项目支持某些格式的签名令牌(不是JWT)。 它作为一个简化的OAuth协议,为网站提供一种简单的身份validationforms。

你可以阅读更多关于它,例如在这里 。

这是相当长的,但大部分是控制器和ASP.NET身份的细节,你可能根本不需要。 最重要的是

第9步:添加对OAuth承载令牌生成的支持

第12步:testing后端API

在那里你可以阅读如何设置你可以从前端访问的端点(例如“/ token”)(以及请求格式的细节)。

其他步骤提供有关如何将该端点连接到数据库等的详细信息,您可以select所需的部分。

有一篇博客文章解释了如何在不使用所有这些复杂的OAuth的情况下使用JWT,也不必担心OWIN与普通的HttpContext类相关的问题。

一探究竟。