如何验证从另一个应用程序生成的 JWT 令牌?

Posted

技术标签:

【中文标题】如何验证从另一个应用程序生成的 JWT 令牌?【英文标题】:How to Validate JWT token generated from one app in another? 【发布时间】:2021-03-14 00:16:42 【问题描述】:

首先,感谢您花时间阅读本文。

我需要在另一个 .net core 3.1 应用程序中验证一个 .net core 2.2 应用程序生成的 JWT 令牌。

目前,我无法使用 .net 核心授权来验证令牌,但能够编写单独的方法来验证令牌。我已确保用于签署令牌的秘密是相同的。

如何使用 .net core 3.1 中的内置身份验证来验证从不同应用程序生成的令牌。

下面是详细说明:

我为登录请求创建了一个 API,该 API 使用此 tutorial 生成 JWT 令牌以在 .net core 2.2 上进行验证。

在我的登录 API 中,在 startup.cs,我添加了如下身份验证:

        var appSettings = appSettingsSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);
        services.AddAuthentication(x =>
        
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        )
        .AddJwtBearer(x =>
        
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            ;
        );

我这样生成令牌:

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
        var tokenDescriptor = new SecurityTokenDescriptor
        
            Subject = new ClaimsIdentity(new Claim[]
            
                new Claim(ClaimTypes.Name, dbUser.UserID.ToString())
            ),
            Expires = DateTime.Now.AddMinutes(_appSettings.Expiry_Min),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
            Audience = _appSettings.Audience,
            Issuer = _appSettings.Issuer,
        ;
        var token = tokenHandler.CreateToken(tokenDescriptor);

JWT 令牌效果很好。在我的控制器中,我添加了 [Authorize] 属性,带有无效令牌的请求会立即被拒绝。

现在,我想在 .net core 3.1 的另一个应用程序中验证我的登录 API 生成的令牌,以验证登录响应。为此,我参考了这个tutorial。在第二个应用程序的 startup.cs 中,我有:

    private void SetupJWTServices(IServiceCollection services)
    
        // configure strongly typed settings objects
        var appSettingsSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingsSection);

        // configure jwt authentication
        var appSettings = appSettingsSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        services.AddAuthentication(x =>
        
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        )
        .AddJwtBearer(x =>
        
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            ;
        );

这个方法,调用如下:

    public void ConfigureServices(IServiceCollection services)
    
        SetupJWTServices(services);            
        services.AddControllers();

        var appSettingsSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingsSection);
    

在我的第二个应用程序中,当我使用 [Authorize] 属性时,请求立即被 401 拒绝,未经授权。但是,当我不使用 [Authorize] 属性并创建验证令牌的方法时,该方法能够验证所有有效请求。我写的方法是:

    private void Validator(string token)
    
        var tokenHandler = new JwtSecurityTokenHandler();
        var appSettingsSection = _appConfiguration.GetSection("AppSettings");
        var appSettings = appSettingsSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);
        try
        
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                ClockSkew = TimeSpan.Zero
            , out SecurityToken validatedToken);

            var jwtToken = (JwtSecurityToken)validatedToken;

            // return account id from JWT token if validation successful
        
        catch
        
            // return null if validation fails
        

这对我来说意味着要验证所有请求,我需要在处理任何内容之前先通过此验证器方法将其传递。但是,这没有使用内置的授权控制。好像不太对。

这是在不同应用程序之间验证 JWT 令牌的方式吗?

【问题讨论】:

您的意思是您可以手动验证令牌,但该令牌可用于请求受保护的端点? @FeiHan 是的,我可以手动验证令牌。我无法使用 .net 核心授权。 我已经使用 IdentityServer4 完成了这项工作。我不认为您可以在不使用 IdentityServer4 库的情况下将您的第一个应用程序用作身份服务器。您的第一个应用程序应该像这样公开一个端点:localhost:5000/.well-known/openid-configuration,第二个应用程序应该从该端点获取配置以配置其算法/设置。 【参考方案1】:

使用声明来验证令牌

static public bool ValidacionTokenManual(string Token, string secretKey, string audienceToken, string issuerToken)
    
        TokenValidationParameters validationParameters = new TokenValidationParameters()
        
            ValidAudience = audienceToken,
            ValidIssuer = issuerToken,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(secretKey))
        ;

        SecurityToken validateToken;
        JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();

        if(handler.CanReadToken(Token))
        
            var user = handler.ValidateToken(Token, validationParameters, out validateToken);

            string roles = user.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Role)?.Value;
            if(roles != "")
            
                return true;
            
        

        return false;
    

【讨论】:

【参考方案2】:

[1] 您没有提及如何将 JWT 传递到您的 API 端点。您只显示了后端配置代码。

您需要为所有客户端请求添加适当的授权标头,格式如下:

Authorization: Bearer token

其中 token 是您的 JWT。您可以使用 .NET core 3.1 中的 AuthenticationHeaderValue 类来执行此操作(见下文)。

您可以通过使用诸如 Fiddler 之类的工具来嗅探 http 流量来验证令牌是否已正确添加到 Authorization 标头中:https://www.telerik.com/fiddler

您可以使用此站点手动验证令牌签名(以确保其结构正确):https://jwt.io/

用于添加 JWT 以更正身份验证标头的示例客户端代码(Razor 页面)(这是针对 .NET Core 3.1,因此您可能需要针对 2.2 稍作修改)

Index.cshtml.cs

public class IndexModel : PageModel

    private static HttpClient Client = new HttpClient();

    [BindProperty]public string Token  get; set; 
    [BindProperty]public string Data  get; set; 
    [BindProperty]public HttpStatusCode HttpCode  get; set; 

    private async Task<string> GetAsync(string uri)
    
        const string serverDomain = "https://localhost:44334/";
        string fullUri = $"serverDomainuri";

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, fullUri);
        if (Token != null)
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Token);

        HttpResponseMessage response = await Client.SendAsync(request);
        HttpCode = response.StatusCode;
        string data = await response.Content.ReadAsStringAsync();

        return data;
    

    public async Task OnGet()
    
        // first get the token
        Token = await GetAsync("api/Main/gettoken");
        
        // now use it to call a method protected with [Authorize]
        Data = await GetAsync("WeatherForecast");
    

索引.cshtml

@page
@model IndexModel
@  ViewData["Title"] = "Home page"; 

<div class="text-center">
  <h1>Test Client</h1>

    <p>Token is @Model.Token</p>
    <p>HttpCode is @((int)Model.HttpCode) (@Model.HttpCode)</p>
    <p>Data is @Model.Data</p>
</div>

[2] 你在使用 ASP.NET Identity SigninManger 吗?

如果是这样,您需要正确设置 TokenValidationParameters.AuthenticationType:

TokenValidationParameters.AuthenticationType = IdentityConstants.ApplicationScheme

如果您提供了您的客户端/前端代码,它将提高您发现问题的机会。

我已经尝试过您提供的后端代码,它在 .NET 3.1 + Microsoft.AspNetCore.Authentication.JwtBearer v3.1.10 上对我来说运行良好

如果您提供了前端生成的示例令牌,我可以告诉您它是否使用 [Authorize] 属性进行验证。

【讨论】:

以上是关于如何验证从另一个应用程序生成的 JWT 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

azure nodejs 网站/webapp 支持用于 JWT 令牌生成和验证的加密算法

JWT身份验证中刷新令牌的正确实现是啥

JWT 令牌基于仪表板应用程序的每个请求的身份验证

使用 JWT 进行环回身份验证

如何验证从 Cognito 获得的 jwt 令牌

如何禁用 JWT 令牌