wake-up-neo.com

Benutzerdefinierte ASP.NET Core JWT Bearer Token-Überprüfung

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:

  1. Gibt es eine Möglichkeit, auf HttpContext innerhalb der CustomJwtSecurityTokenHandler-Klasse zuzugreifen, sodass ich benutzerdefinierte Validierungen basierend auf dem aktuellen Client/Anforderer hinzufügen kann?
  2. Gibt es eine andere Möglichkeit, die Echtheit des Anforderers mithilfe einer solchen Methode/Middleware zu überprüfen?
7
Sang Suantak

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);
    ...
}
4
Set

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!

0
Patrick

Da ich nirgendwo eine Antwort finden konnte, habe ich die Validierungslogik von HttpContext auf eine ActionFilter verschoben.

Die Lösung wird jedoch zerstreut.

0
Sang Suantak