C# 生成 JWT 令牌,验证签名应该放在哪里
在使用C#生成JWT令牌时,签名验证应该放在哪里的问题是一个常见的问题。我在4年前回答了一个与此相关的问题,当时我使用了HMAC进行身份验证。然而,现在安全性发生了很多变化,特别是JWT变得越来越流行。在这篇文章中,我将尝试以尽可能简单和基础的方式解释如何使用JWT,以免在OWIN、Oauth2、ASP.NET Identity等方面迷失方向。
首先,我们需要了解JWT令牌的基本结构和工作原理。一个JWT令牌由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。头部和负载是以Base64编码的JSON格式字符串,而签名是通过使用头部和负载的内容进行签名生成的,也是以Base64编码的字符串。整个JWT令牌的格式如下:
. .
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
在使用JWT进行身份验证时,我们需要在生成令牌时添加签名,并在验证令牌时进行签名验证。签名验证应该放在令牌验证的最后一步,以确保令牌的完整性和安全性。在验证签名之前,我们需要先对令牌的其他部分进行验证,例如检查令牌是否已过期、是否由可信任的发行者签发等。
在C#中生成JWT令牌的过程中,我们可以使用Microsoft提供的`System.IdentityModel.Tokens.Jwt` NuGet包。下面是一个生成JWT令牌的示例代码:
using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; public class JwtManager { 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; } }
上述代码中的`Secret`变量是一个用于生成签名的密钥,需要保密存储。在生成JWT令牌时,我们可以调用`GenerateToken`方法并指定用户名和过期时间,该方法将返回生成的JWT令牌。
在验证JWT令牌时,我们可以使用Microsoft提供的`JwtSecurityTokenHandler`类。下面是一个验证JWT令牌的示例代码:
using System; using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; public class JwtAuthenticationAttribute : Attribute, IAuthenticationFilter { private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw=="; public bool AllowMultiple => false; public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) { string token; if (TryRetrieveToken(context.Request, out token)) { try { var principal = ValidateToken(token); if (principal != null) { context.Principal = principal; } } catch (Exception) { // Token validation failed } } return Task.FromResult(0); } public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) { return Task.FromResult(0); } private bool TryRetrieveToken(HttpRequestMessage request, out string token) { token = null; IEnumerableauthHeaders; if (request.Headers.TryGetValues("Authorization", out authHeaders) && authHeaders.Count() > 0) { var bearerToken = authHeaders.ElementAt(0); token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken; return true; } return false; } private ClaimsPrincipal ValidateToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); 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; } }
上述代码中的`JwtAuthenticationAttribute`类是一个自定义的身份验证属性,用于验证JWT令牌。在`AuthenticateAsync`方法中,我们首先尝试从请求中提取JWT令牌,然后调用`ValidateToken`方法进行验证。如果令牌验证成功,我们将设置`context.Principal`属性为验证通过的主体。
最后,在使用JWT进行身份验证时,我们需要在Web API的全局范围内添加授权过滤器,以防止任何未经授权的请求访问受保护的资源。我们可以在`WebApiConfig.cs`文件中添加以下代码:
config.Filters.Add(new AuthorizeAttribute());
这样,只有带有有效JWT令牌的请求才能访问受保护的资源。
总结起来,使用C#生成JWT令牌并验证签名的步骤如下:
1. 在生成JWT令牌时,使用密钥对令牌进行签名。
2. 在验证JWT令牌时,首先对令牌的其他部分进行验证,例如检查令牌是否已过期、是否由可信任的发行者签发等。
3. 最后,验证令牌的签名,以确保令牌的完整性和安全性。
通过以上步骤,我们可以使用C#生成并验证JWT令牌,从而实现简单而安全的身份验证机制。
问题的原因是作者想要在C#中生成JWT令牌并验证签名的位置。解决方法是使用OWIN的Startup.cs文件和Microsoft.Owin.Security.Jwt库来实现。
首先,在Web.config文件中进行一些修改,以便应用程序能够访问Startup.cs文件。
然后,创建一个Startup类,在其中配置JWT验证。使用JwtBearerAuthenticationOptions类的实例来设置验证参数,包括有效的受众、发行者和签名密钥等。
最后,在Configuration方法中使用app.UseJwtBearerAuthentication方法将JWT验证中间件添加到应用程序中。
另外,作者还分享了一个名为ConfigHelper的类,用于获取配置信息。这个类包含了获取发行者、受众、签名凭证和安全密钥等的方法。
最后,作者还展示了如何在typescript中发送JWT令牌。在typescript代码中,通过Authorization头部字段将令牌发送给后端API。
问题的解决方法就是使用OWIN的Startup.cs文件和Microsoft.Owin.Security.Jwt库来生成和验证JWT令牌,同时使用ConfigHelper类获取配置信息。
C#生成JWT令牌,在哪里验证签名应该放置?
问题的原因是在使用JWT令牌进行身份验证时,需要确保令牌的签名有效。这是为了防止伪造令牌和保护用户身份的安全性。
解决方法是在生成JWT令牌时,将签名的验证放置在令牌的生成过程中。在给定的代码示例中,签名验证位于以下代码段中:
var tokenHandler = new JwtSecurityTokenHandler(); var encryptionkey = Configuration["Jwt:Encryptionkey"]; var key = Encoding.ASCII.GetBytes(encryptionkey); var tokenDescriptor = new SecurityTokenDescriptor { Issuer = Configuration["Jwt:Issuer"], Subject = Claims, Expires = DateTime.UtcNow.AddMinutes(Convert.ToDouble(Configuration["Jwt:ExpiryTimeInMinutes"])), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token);
在这段代码中,`SigningCredentials`表示签名的凭证,使用`SymmetricSecurityKey`表示对称密钥,使用`HmacSha256Signature`表示签名算法。这样可以确保生成的JWT令牌具有有效的签名。
通过将这些代码添加到ASP.NET Core Web API的身份验证服务中,并在需要授权的方法上添加策略过滤器,可以实现JWT令牌的生成和验证。这样可以确保用户身份的安全性和授权访问的有效性。