当您使用asp.net身份拥有单独的Identity项目时,如何在其他项目中验证JWT令牌

时间:2019-08-05 08:08:24

标签: c# asp.net-core .net-core asp.net-identity jwt-auth

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

在邮递员中调用api存储路由时遇到此错误。我在“授权”标头中传递令牌。

  

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

     

IOException:IDX20804:无法从以下位置检索文档:“ [PII是   隐]'。   Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string   地址,CancellationToken取消)

     

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

     

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

     

显示原始异常详细信息System.Net.Http.HttpRequestException:   响应状态代码不表示成功:404(未找到)。在   System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()在   Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String   地址,CancelationToken取消)IOException:IDX20804:无法   从以下位置检索文档:[[PII隐藏]。   Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string   地址,CancelationToken取消)   Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(string   地址,IDocumentRetriever检索器,CancellationToken取消)   Microsoft.IdentityModel.Protocols.ConfigurationManager.GetConfigurationAsync(CancellationToken   取消)

     

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

这是我在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

1 个答案:

答案 0 :(得分:1)

Asp.NET标识本身并不完全适合该用例。您可能要看起来像Identity Server,它扩展了Identity的功能。它允许您以多种方式验证令牌,

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