用于Asp.Net Web Api的JWTauthentication
我试图在我的web api应用程序中支持JWT不记名令牌(Json Web令牌),我迷路了。
我看到.net core
和OWIN
应用程序的支持。
我目前通过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令牌有三个部分:
- 标题:被编码为base64的JSON格式
- 声明:被编码为base64的JSON格式。
- 签名:根据编码为base64的标题和声明创build和签名。
如果您使用带有上述标记的网站jwt.io ,则可以解码并看到如下所示的标记:
从技术上说,JWT使用从头文件和声明中签名的签名,并在头文件中指定安全algorithm(例如:HMACSHA256)。 因此,如果您将任何敏感信息存储在声明中,则需要通过HTTP传输JWT。
现在,为了使用JWTauthentication,如果您有遗留的Web Api系统,您并不需要OWIN中间件。 简单的概念是如何提供JWT令牌以及在请求到达时如何validation令牌。 而已。
回到演示,为了保持JWT令牌轻量级,我只在JWT中存储username
和expiration 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类相关的问题。
一探究竟。