Nach langem Lesen habe ich einen Weg gefunden, einen benutzerdefinierten JWT-Träger-Token-Validator wie folgt zu implementieren.
Starup.cs
Codes:
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<TokenProviderMiddleware>(Options.Create(tokenProviderOptions));
}
Unten ist die benutzerdefinierte Validator-Klasse:
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;
}
}
Im Falle eines gestohlenen Tokens möchte ich eine zusätzliche Sicherheitsebene hinzufügen, um zu bestätigen, dass die Anforderung von demselben Client stammt, der das Token generiert hat.
Fragen:
HttpContext
innerhalb der CustomJwtSecurityTokenHandler
-Klasse zuzugreifen, sodass ich benutzerdefinierte Validierungen basierend auf dem aktuellen Client/Anforderer hinzufügen kann?In ASP.NET Core konnte HttpContext
über den IHttpContextAccessor
-Dienst abgerufen werden. Verwenden Sie DI, um die IHttpContextAccessor
-Instanz an Ihren Handler zu übergeben und den Wert der IHttpContextAccessor.HttpContext
-Eigenschaft abzurufen.
Der IHttpContextAccessor
-Dienst ist von defaul nicht registriert. Daher müssen Sie zunächst Folgendes in Ihre Startup.ConfigureServices
-Methode einfügen:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Ändern Sie dann Ihre CustomJwtSecurityTokenHandler
-Klasse:
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;
}
Sie sollten die DI-Technik auch für die Instantiierung JwtSecurityTokenHandler
verwenden. Schauen Sie in Dependency Injection documentation nach, wenn Sie mit all diesen Dingen neu sind.
Update: Wie man Abhängigkeiten manuell auflöst (mehr Infos hier )
Configure
-Methode ändern, um IServiceProvider serviceProvider
zu verwenden:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory, IApplicationLifetime appLifetime,
IServiceProvider serviceProvider)
{
...
var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
// and extend ConfigureAuth
ConfigureAuth(app, httpContextAccessor);
...
}
Für einen benutzerdefinierten JWT-Validator habe ich eine JWTCosumerProvider-Klasse erstellt, die an IOAuthBearerAuthenticationProvider gesendet wird. Und implementieren Sie die ValidateIdentity () - Methode, um den Identitätsanspruch zu überprüfen, nach dem ich die IP-Adresse des Clients an erster Stelle gespeichert habe. Anschließend wird die aktuelle ID-Adresse der Anfrage verglichen.
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<object>(null);
}
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"];
}
ich hoffe das hilft!
Da ich nirgendwo eine Antwort finden konnte, habe ich die Validierungslogik von HttpContext
auf eine ActionFilter
verschoben.
Die Lösung wird jedoch zerstreut.