首页 > 解决方案 > 当您使用 asp.net 身份有单独的身份项目时如何在其他项目中验证 JWT 令牌

问题描述

我有两个解决方案。一种是使用 asp.net 身份生成 JWT 令牌进行身份管理。在第二个项目中,有一些 API 是安全的,并且可以验证从身份项目生成的令牌,但令牌验证不起作用。

在邮递员中调用 api store route 时出现此错误。我在授权标头中传递令牌。

处理请求时发生未处理的异常。HttpRequestException:响应状态码不表示成功:404(未找到)。System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()

IOException:IDX20804:无法从以下位置检索文档:“[PII 已隐藏]”。Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(字符串地址,CancellationToken 取消)

InvalidOperationException:IDX20803:无法从“[PII 隐藏]”获取配置。Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken 取消)

堆栈查询 Cookie 标头 HttpRequestException:响应状态代码不表示成功:404(未找到)。System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(字符串地址,CancellationToken 取消)

显示原始异常详细信息 System.Net.Http.HttpRequestException:响应状态代码不表示成功:404(未找到)。在 System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() 在 Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) IOException: IDX20804: Unable to retrieve document from: '[PII is hidden]'。Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel) Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(string address, IDocumentRetriever retriever, CancellationToken cancel) Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel)

显示原始异常详细信息 System.IO.IOException:IDX20804:无法从以下位置检索文档:“[PII 已隐藏]”。---> System.Net.Http.HttpRequestException:响应状态码不表示成功:404(未找到)。在 System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() 在 Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- 内部异常堆栈跟踪结束 --- 在 Microsoft.IdentityModel.Protocols.HttpDocumentRetriever。在 Microsoft.IdentityModel.Protocols.ConfigurationManager`1 的 Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(字符串地址,IDocumentRetriever 检索器,CancellationToken 取消)处的 GetDocumentAsync(字符串地址,CancellationToken 取消)。GetConfigurationAsync(CancellationToken cancel)InvalidOperationException:IDX20803:无法从“[PII 隐藏]”获取配置。Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel) Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationHandler.AuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext 上下文,字符串方案) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)无法从以下位置获取配置:“[PII 已隐藏]”。Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel) Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationHandler.AuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext 上下文,字符串方案) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)无法从以下位置获取配置:“[PII 已隐藏]”。Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken cancel) Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationHandler.AuthenticateAsync() Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext 上下文,字符串方案) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)

显示原始异常详细信息 System.InvalidOperationException:IDX20803:无法从以下位置获取配置:“[PII 已隐藏]”。---> System.IO.IOException:IDX20804:无法从以下位置检索文档:“[PII 已隐藏]”。---> System.Net.Http.HttpRequestException:响应状态码不表示成功:404(未找到)。在 System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() 在 Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- 内部异常堆栈跟踪结束 --- 在 Microsoft.IdentityModel.Protocols.HttpDocumentRetriever。 Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address,1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) 在 Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() 在 Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() 在 Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync() 在Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) 上的 Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

这是我在 Identity 项目中的 Startup.cs 类。

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cosmonaut;
using Cosmonaut.Extensions.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Triverse.Identity.Models;
using Triverse.Identity.Services;
using IdentityRole = Microsoft.AspNetCore.Identity.DocumentDB.IdentityRole;

namespace Triverse.Identity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var endPointUri = Configuration.GetValue<string>("AppSettings:EndpointUri");
            var primaryKey = Configuration.GetValue<string>("AppSettings:PrimaryKey");
            var databaseId = Configuration.GetValue<string>("AppSettings:DatabaseId");
            var collectionId = Configuration.GetValue<string>("AppSettings:CollectionId");

            var client = new DocumentClient(new Uri(endPointUri), primaryKey);
            services.AddSingleton<IDocumentClient>(client);

            // make sure the database exists!
            var db = client.CreateDatabaseQuery().Where(d => d.Id == databaseId).AsEnumerable().FirstOrDefault()
                     ?? client.CreateDatabaseAsync(new Database { Id = databaseId }).Result;

            var databaseLink = db.SelfLink;

            services.AddIdentityWithDocumentDBStores<ApplicationUser, IdentityRole>(
                dbOptions =>
                {
                    dbOptions.DocumentUrl = endPointUri;
                    dbOptions.DocumentKey = primaryKey;
                    dbOptions.DatabaseId = databaseId;
                    dbOptions.CollectionId = collectionId;
                },
                identityOptions =>
                {
                    identityOptions.User.RequireUniqueEmail = true;
                });

            var cosmosSettings = new CosmosStoreSettings(databaseId, endPointUri, primaryKey);
            services.AddCosmosStore<ApplicationUser>(cosmosSettings);

            services.AddScoped<IAccountRepository, AccountRepository>();
            services.AddTransient<ITokenService, TokenService>();

            services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(cfg =>
                {
                    cfg.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = Configuration["Tokens:Issuer"],
                        ValidAudience = Configuration["Tokens:Audience"],
                        IssuerSigningKey =
                            new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
                        ValidateLifetime = true
                    };

                    cfg.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("Token-Expired", "true");
                                context.Response.Headers.Add("access-control-expose-headers", "Token-Expired");
                            }

                            return Task.CompletedTask;
                        }
                    };
                });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthentication();

            app.UseMvc();
        }
    }
}

这是我在 Identity 项目中的 appsettings.json。

"Tokens": {
    "Key": "4343@!#ewewq",
    "Issuer": "http://localhost:44376/",
    "Audience": "http://localhost:44385/",
    "ExpiryMinutes": "55",
    "ValidateLifetime": true
  }

这是我在 Store 项目中的 Startup.cs 类。

using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cosmonaut;
using Cosmonaut.Extensions.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Store.API.Services;

namespace Store.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            var endPointUri = Configuration.GetValue<string>("AppSettings:EndpointUri");
            var primaryKey = Configuration.GetValue<string>("AppSettings:PrimaryKey");
            var databaseId = Configuration.GetValue<string>("AppSettings:DatabaseId");

            var client = new DocumentClient(new Uri(endPointUri), primaryKey);
            services.AddSingleton<IDocumentClient>(client);

            var db = client.CreateDatabaseQuery().Where(d => d.Id == databaseId).AsEnumerable().FirstOrDefault()
                     ?? client.CreateDatabaseAsync(new Database {Id = databaseId}).Result;

            var databaseLink = db.SelfLink;

            var cosmosSettings = new CosmosStoreSettings(databaseId, endPointUri, primaryKey);
            services.AddCosmosStore<Models.Store>(cosmosSettings);

            services.AddScoped<IStoreRepository, StoreRepository>();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(options =>
            {
                options.Authority = "http://localhost:44376/";
                options.RequireHttpsMetadata = false;
                options.Audience = "http://localhost:44385/";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseAuthentication();

            app.UseMvc();
        }
    }
}

这是我在 Store 项目中的控制器方法。

[HttpGet]
[Authorize]
public async Task<IActionResult> GetStores()
{
  var stores = new
  {
     Id = 1,
     Name = "T-Shirt",
     Price = "120.00"
  };

  return Ok(stores);
}

这是我从 Identity 项目生成的 JWT 令牌。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzaGFAZG9tYWluLmNvbSIsImp0aSI6IjFjYjNkNjA2LWI4MGQtNGNlZC1hMWFjLThlYmUzNzc1ZGViOSIsIlVuaXF1ZUlkIjoiZjU1ZTM2MWQtYjFkYy00MDg4LTlmYjQtMDg3ZTI4OTFjNWI1IiwidW5pcXVlX25hbWUiOiJzaGFAZG9tYWluLmNvbSIsImZpcnN0TmFtZSI6IlNoYXduIiwibmJmIjoxNTY0OTkwMjA1LCJleHAiOjE1NjQ5OTM1MDUsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNzYvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM4NS8ifQ.ZBK8Fi14QUc9ObZx7ojg7LPcl8Qs2vrQyhZi7Dbk4Gg

标签: c#asp.net-core.net-coreasp.net-identityjwt-auth

解决方案


Asp.NET Identity 本身并不适用于这个用例。您可能希望看起来像 Identity Server,它扩展了 Identity 的功能。它允许您以多种方式验证令牌,

  1. 它有一个用于验证令牌的端点
  2. 它公开了一个众所周知的配置端点,该端点提供(以及许多其他东西)用于签署令牌的 RSA 证书的公钥。因此,如果您愿意,您可以自己验证签名。

推荐阅读