从 TokenValidatedContext 获取签名

Posted

技术标签:

【中文标题】从 TokenValidatedContext 获取签名【英文标题】:get signature from TokenValidatedContext 【发布时间】:2020-11-19 20:47:52 【问题描述】:

我正在为我的 .NET Core 项目使用 Microsoft.AspNetCore.Authentication.JwtBearerSystem.IdentityModel.Tokens.Jwt 包。

在配置服务时,我正在向OnTokenValidated 事件添加逻辑。

    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(jwtBearerOptions =>
        
            // ... set TokenValidationParameters ...

            jwtBearerOptions.Events = new JwtBearerEvents()
            
                OnTokenValidated = (tokenValidatedContext) => 
                
                    JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
                    string tokenWithoutSignature = jwtSecurityTokenHandler.WriteToken(tokenValidatedContext.SecurityToken);

                    // ... full token from request? ...
                
            ;
        );

因为我知道上下文只返回没有签名的令牌,所以我想知道我该怎么做

要么获取带有签名的完整令牌 或另外将签名添加到tokenWithoutSignature 字符串中

如果这不可能:

我正在以这种方式生成新令牌

public string GenerateAccessToken(IDictionary<string, object> payload)

    SymmetricSecurityKey symmetricSecurityKey = new SymmetricSecurityKey(Convert.FromBase64String("secret from config"));

    SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
    
        Claims = payload,
        Expires = DateTime.Now, // value from config
        SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature)
    ;

    JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
    SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
    string token = tokenHandler.WriteToken(securityToken);

    return token;

也许我可以检索

没有签名的令牌字符串 只有签名

在这个方法内?


如果没有任何效果:

因为我知道不记名令牌总是包含三个部分,例如

header.payload.signature

我可以将字符串段拆分为一个数组,从数组中取出第一个和第二个元素并创建一个新的字符串

firstString + . + 第二个字符串

那应该给我没有签名的令牌。有没有更好的想法来从完整的令牌中删除签名?


我为什么要实现这个目标?

这个问题是基于这个问题

Security token from TokenValidatedContext from the OnTokenValidated event listener is missing last string segment

我正在使用访问和刷新令牌。在验证期间,我必须将请求中的令牌与数据库中的令牌进行比较。数据库中的令牌也包含签名。所以我面临与上面链接相同的问题。

这就是为什么我想到了多种解决方案并将它们写在这里。如果TokenValidatedContext 无法将签名返回给我,似乎我必须将 JWT 存储到数据库中而不需要签名。而且对于这种情况,我需要将签名与生成的 JWT 分开。

没有使用刷新令牌我只将用户的最长会话生命周期存储到数据库中。流程就是基于这个想法

Only store the time of the JWT with the highest lifetime to the database instead of the whole JWT

使用刷新令牌我想出了以下流程。 既然您知道 OnTokenValidated 处理验证逻辑,以下逻辑是额外的。我有一个数据库表

用户名 |访问令牌 |刷新令牌 | refresh_token_expires_at

主键是用户名+access_token的组合键。刷新令牌只是像这样生成的一些随机字符串

public string GenerateRefreshToken()

    var randomNumber = new byte[32];

    using (var rng = RandomNumberGenerator.Create())
    
        rng.GetBytes(randomNumber);

        return Convert.ToBase64String(randomNumber);
    

这就是为什么我要为它存储一个额外的到期日期。它应该可以在一段时间后过期。

登录

将用户生成的访问和刷新令牌及其到期时间存储到数据库中。将完整访问令牌或无签名访问令牌存储到数据库(取决于此问题的解决方案)。

命中受保护的端点

检查该用户的访问令牌是否存在于数据库中。

点击刷新端点

检查数据库刷新令牌是否已过期。如果不是,请将其与请求中的刷新令牌进行比较。如果一切正常,从数据库中删除旧的访问和刷新令牌,并将新生成的访问和刷新令牌存储到数据库中。

退出

从数据库中删除该访问及其连接的刷新令牌。

【问题讨论】:

【参考方案1】:

我不太明白你为什么要这样做,但如果你只需要原始令牌,你可以使用以下之一:

o.Events = new JwtBearerEvents

    OnTokenValidated = (context) =>
    
        var securityToken = (System.IdentityModel.Tokens.Jwt.JwtSecurityToken)context.SecurityToken;
        var token = securityToken.RawData; // "ey...."
        var tokenHeader = securityToken.RawHeader; // "ey...."
        var tokenPayload = securityToken.RawPayload; // "ey...."
        var tokenSignatur = securityToken.RawSignature; // "ey...."
        var fullBearerHeader = context.Request.Headers["Authorization"]; // "Bearer ey..."
        return Task.CompletedTask;
    
;

你可能想让代码在类型转换等方面更安全一些,但它应该给你令牌。

【讨论】:

【参考方案2】:

为什么要操纵令牌?如果只是为了验证令牌,您可以使用下面的代码。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)    
.AddJwtBearer(options =>    
    
    options.TokenValidationParameters = new TokenValidationParameters    
        
        ValidateIssuer = true,    
        ValidateAudience = true,    
        ValidateLifetime = true,    
        ValidateIssuerSigningKey = true,    
        //ValidIssuer = Configuration["Issuer"],    
        //ValidAudience = Configuration["Audience"],    
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Key"]))    
    ;    
);    

public void Configure(IApplicationBuilder app, IHostingEnvironment env)    
    
    app.UseAuthentication();    
    app.UseMvc();    
 

【讨论】:

感谢您的回复。我已经实现了你的解决方案。我更新了我的问题并添加了一个附加部分 您是否打算在每个请求中刷新访问令牌? 否,但由于我将访问和刷新令牌存储到数据库(一个刷新令牌连接到一个访问令牌),我必须将数据库访问令牌与上下文中的令牌进行比较。但是这个上下文返回一个没有签名的令牌...... 我更新了我的问题,我希望这能让事情变得清晰:)

以上是关于从 TokenValidatedContext 获取签名的主要内容,如果未能解决你的问题,请参考以下文章

Scrapy 从其他链接获取值

从一个触发器,如何找出谁修改了表X上的数据,同时该用户从一个通用的dbuser登录,但从用户表获得了权利

为啥我们要包装 HttpServletRequest ? api 提供了一个 HttpServletRequestWrapper 但我们从包装请求中获得了啥?

通过 twitter 从 Digits 验证电话号码后,我在最近的活动中获得了我的应用程序的两个实例

从脑瘫患者重获交流到免开颅微创,脑机接口更安全了吗?

如何从零基础转行大数据工程师暂获年薪30W,看阿里云大牛怎么说