c# - 当您使用 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.ConfigurationManager
1.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
解决方案
Asp.NET Identity 本身并不适用于这个用例。您可能希望看起来像 Identity Server,它扩展了 Identity 的功能。它允许您以多种方式验证令牌,
- 它有一个用于验证令牌的端点
- 它公开了一个众所周知的配置端点,该端点提供(以及许多其他东西)用于签署令牌的 RSA 证书的公钥。因此,如果您愿意,您可以自己验证签名。
推荐阅读
- haskell - 组合任一类型变量
- recursion - 我正在尝试编写一个尾随值,该值仅在 ThinkScript 中满足条件时才调整为新值
- javascript - 由 src Element 选择时通过类重置获取元素
- python - Python IDLE 重启
- reactjs - (response.ok === false) { setError('show')
- python - 如何在 Python 中创建 3d 磁场的可视化表示?
- python - “NameError:当我尝试在 Python3 中使用该变量时,未定义名称‘响应’
- powershell - 在 rdlc 报告中使用表达式时需要哪个 Rreportviewer dll
- javascript - 类和模块之间的JS数据传输
- c# - .Netcore WebApi 将用户错误返回给要显示给用户的 vue 应用程序......可能已经将自己编码到角落寻找更好的答案