强制身份验证中间件仅接受加密令牌

时间:2018-11-27 17:37:06

标签: c# authentication .net-core jwt netcoreapp2.1

根据this question,我使用下面的代码片段对JWToken进行签名和加密。

var claims = new Claim[] { new SomeClaimes() };
var scKey = Encoding.UTF8.GetBytes("SOME KEY");
var ecKeyTemp = Encoding.UTF8.GetBytes("SOME OTHER KEY");

byte[] ecKey = new byte[256 / 8];
Array.Copy(ecKeyTemp, ecKey, 256 / 8);

var tokenDescriptor = new SecurityTokenDescriptor {
    Subject = new ClaimsIdentity(claims),
    SigningCredentials = new SigningCredentials(
        new SymmetricSecurityKey(
            scKey),
            SecurityAlgorithms.HmacSha512),
    EncryptingCredentials = new EncryptingCredentials(
        new SymmetricSecurityKey(
            ecKey),
            SecurityAlgorithms.Aes256KW,
            SecurityAlgorithms.Aes256CbcHmacSha512), 
    Issuer = "My Jwt Issuer",
    Audience = "My Jwt Audience",
    IssuedAt = DateTime.UtcNow,
    Expires = DateTime.Now.AddDays(7),
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
var jwt = tokenHandler.WriteToken(token);

这是我的服务注册:

services
.AddAuthentication(o => {
    o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(cfg => {
    cfg.RequireHttpsMetadata = false;
    cfg.SaveToken = true;
    cfg.TokenValidationParameters = new TokenValidationParameters {
        ValidIssuer = "My Jwt Issuer",
        ValidAudience = "My Jwt Audience",
        IssuerSigningKey = new SymmetricSecurityKey(SameKeyAsGenerating)),
        TokenDecryptionKey = new SymmetricSecurityKey(SameKeyAsGenerating),
        ClockSkew = TimeSpan.Zero, 
        RequireSignedTokens = true,
        RequireExpirationTime = true,
        SaveSigninToken = true,
        ValidateActor = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidateIssuerSigningKey = true,
        ValidateLifetime = true,
        ValidateTokenReplay = true,
    };
});

问题是身份验证层将授权的加密令牌和未加密令牌都进行身份验证。我的意思是,当我创建不带EncryptingCredentials的令牌(只是签名令牌,未加密)时,该令牌仍然有效并且请求得到授权。问题是如何强制身份验证层仅接受经过签名的加密令牌,而拒绝刚刚签名的未加密令牌?

更新:解决方案:

由于sellotape's answer,我实现了JwtEncryptedSecurityTokenHandler

public class JwtEncryptedSecurityTokenHandler : JwtSecurityTokenHandler {

    [DebuggerStepThrough]
    public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters, out SecurityToken validatedToken) {
        if (string.IsNullOrWhiteSpace(token))
            throw new ArgumentNullException(nameof (token));

        if (validationParameters == null)
            throw new ArgumentNullException(nameof (validationParameters));

        if (token.Length > MaximumTokenSizeInBytes)
            throw new ArgumentException(
                $"IDX10209: token has length: '{token.Length}' which is larger than the MaximumTokenSizeInBytes: '{MaximumTokenSizeInBytes}'.");

        var strArray = token.Split(new[] { '.' }, 6);
        if (strArray.Length == 5)
            return base.ValidateToken(token, validationParameters, out validatedToken);

        throw new SecurityTokenDecryptionFailedException();
    }
}

并在Startup.ConfigureServices()中使用了新的处理程序:

.AddJwtBearer(cfg => {
    cfg.RequireHttpsMetadata = false;
    // other configurations...
    cfg.SecurityTokenValidators.Clear();
    cfg.SecurityTokenValidators.Add(new JwtEncryptedSecurityTokenHandler());
});

有关更多说明,请参见接受的答案。

1 个答案:

答案 0 :(得分:1)

我敢肯定有几种方法可以做到这一点,但是如何做:

  • 实现ISecurityTokenValidator,可能继承自SecurityTokenHandler;所需的覆盖非常简单,可以很大程度上从JwtSecurityTokenHandler复制。覆盖ValidateToken()并在JWT未加密的情况下抛出**。

  • AddJwtBearer() configure-options操作中添加新的处理程序/验证器:cfg.SecurityTokenValidators.Add(new RequireEncryptedTokenHandler());


**如果您查看source for JwtSecurityTokenHandler.ValidateToken()(不确定所使用的版本,但我认为消息是相同的),似乎“已加密”被简单地确定为“具有5个部分” ”,那么在新处理程序中应该易于实现(复制/粘贴)。