ASP.NET核心JWT承载令牌自定义validation
经过大量阅读,我找到了一种实现自定义JWT承载令牌validation器的方法,如下所示。
Starup.cs
代码:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseStaticFiles(); app.UseIdentity(); ConfigureAuth(app); app.UseMvcWithDefaultRoute(); } private void ConfigureAuth(IApplicationBuilder app) { var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("TokenAuthentication:SecretKey").Value)); var tokenValidationParameters = new TokenValidationParameters { // The signing key must match! ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, // Validate the JWT Issuer (iss) claim ValidateIssuer = true, ValidIssuer = Configuration.GetSection("TokenAuthentication:Issuer").Value, // Validate the JWT Audience (aud) claim ValidateAudience = true, ValidAudience = Configuration.GetSection("TokenAuthentication:Audience").Value, // Validate the token expiry ValidateLifetime = true, // If you want to allow a certain amount of clock drift, set that here: ClockSkew = TimeSpan.Zero }; var jwtBearerOptions = new JwtBearerOptions(); jwtBearerOptions.AutomaticAuthenticate = true; jwtBearerOptions.AutomaticChallenge = true; jwtBearerOptions.TokenValidationParameters = tokenValidationParameters; jwtBearerOptions.SecurityTokenValidators.Clear(); //below line adds the custom validator class jwtBearerOptions.SecurityTokenValidators.Add(new CustomJwtSecurityTokenHandler()); app.UseJwtBearerAuthentication(jwtBearerOptions); var tokenProviderOptions = new TokenProviderOptions { Path = Configuration.GetSection("TokenAuthentication:TokenPath").Value, Audience = Configuration.GetSection("TokenAuthentication:Audience").Value, Issuer = Configuration.GetSection("TokenAuthentication:Issuer").Value, SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) }; app.UseMiddleware(Options.Create(tokenProviderOptions)); }
以下是自定义validation器类:
public class CustomJwtSecurityTokenHandler : ISecurityTokenValidator { private int _maxTokenSizeInBytes = TokenValidationParameters.DefaultMaximumTokenSizeInBytes; private JwtSecurityTokenHandler _tokenHandler; public CustomJwtSecurityTokenHandler() { _tokenHandler = new JwtSecurityTokenHandler(); } public bool CanValidateToken { get { return true; } } public int MaximumTokenSizeInBytes { get { return _maxTokenSizeInBytes; } set { _maxTokenSizeInBytes = value; } } public bool CanReadToken(string securityToken) { return _tokenHandler.CanReadToken(securityToken); } public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) { //How to access HttpContext/IP address from here? var principal = _tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken); return principal; } }
如果被盗令牌,我想添加一个额外的安全层来validation请求是来自生成令牌的同一客户端。
问题:
- 有没有办法我可以访问
CustomJwtSecurityTokenHandler
类中的HttpContext
,以便我可以添加基于当前客户端/请求者的自定义validation? - 有没有其他方法可以使用这样的方法/中间件validation请求者的真实性?
在ASP.NET Core中,可以使用IHttpContextAccessor
服务获取HttpContext
。 使用DI将IHttpContextAccessor
实例传递到您的处理程序并获取IHttpContextAccessor.HttpContext
属性的值。
默认情况下, IHttpContextAccessor
服务未注册,因此您首先需要在Startup.ConfigureServices
方法中添加以下内容:
services.TryAddSingleton();
然后修改CustomJwtSecurityTokenHandler
类:
private readonly IHttpContextAccessor _httpContextAccessor; public CustomJwtSecurityTokenHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; _tokenHandler = new JwtSecurityTokenHandler(); } ... public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) { var httpContext = _httpContextAccessor.HttpContext; }
您还应该使用DI技术进行JwtSecurityTokenHandler
实例化。 如果您不熟悉所有这些内容,请查看dependency injection文档。
更新:如何手动解决依赖关系( 此处有更多信息)
修改Configure
方法以使用IServiceProvider serviceProvider
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime, IServiceProvider serviceProvider) { ... var httpContextAccessor = serviceProvider.GetService(); // and extend ConfigureAuth ConfigureAuth(app, httpContextAccessor); ... }
由于我无法在任何地方找到答案,因此我将有关HttpContext
的validation逻辑移至ActionFilter
。
但是,它确实使解决方案分散。
对于自定义JWTvalidation器,我在IOAuthBearerAuthenticationProvider中创建了一个JWTCosumerProvider类。 并实现ValidateIdentity()方法来检查我在第一时间存储客户端IP地址的身份声明,然后与之后的当前请求ID地址进行比较。
public Task ValidateIdentity(OAuthValidateIdentityContext context) { var requestIPAddress = context.Ticket.Identity.FindFirst(ClaimTypes.Dns)?.Value; if (requestIPAddress == null) context.SetError("Token Invalid", "The IP Address not right"); string clientAddress = JWTHelper.GetClientIPAddress(); if (!requestIPAddress.Equals(clientAddress)) context.SetError("Token Invalid", "The IP Address not right"); return Task.FromResult
JWTHelper.GetClientIPAddress()
internal static string GetClientIPAddress() { System.Web.HttpContext context = System.Web.HttpContext.Current; string ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (!string.IsNullOrEmpty(ipAddress)) { string[] addresses = ipAddress.Split(','); if (addresses.Length != 0) { return addresses[0]; } } return context.Request.ServerVariables["REMOTE_ADDR"]; }
希望这个帮助!