首页 > 解决方案 > 由于 CORS 问题,无法访问 API

问题描述

使用 Asp.Net Core 5 和OpenIdDict for OpenId 我有 3 个应用程序:

Auth with OpenIdDict running on https://localhost:5000
API running on https://localhost:5001
SPA running on https://localhost:5002

从我的 Angular 的 SPA 中,我可以登录和注销。

我能够访问允许匿名访问的 API 端点。

如果我尝试在不发送访问令牌的情况下访问需要身份验证的 API 端点,我会按预期收到 401 错误。

问题

当我尝试访问需要身份验证的 API 端点并在授权标头中发送访问令牌时,我收到错误消息:

Access to XMLHttpRequest at 'https://localhost:5001/v1.0/posts' from origin 'https://localhost:5002' has been blocked by CORS policy: 

No 'Access-Control-Allow-Origin' header is present on the requested resource.

POST https://localhost:5001/v1.0/posts

更新

以防万一我的 Angular 请求是:

httpClient.post(`https://localhost:5001/v1.0/posts`, 
 { title: "My post", body: "Some text" }, 
 { headers: { 
     'Content-Type': 'application/json', 
     'Authorization': `Bearer ${user.access_token}` 
   } 
 }).subscribe();

API 启动 ConfigureServices和方法Configure是:

public void ConfigureServices(IServiceCollection services) {

  services
    .AddControllers()
    .SetCompatibilityVersion(CompatibilityVersion.Latest)
    .AddJsonOptions()
    .AddFluentValidation();

  services
    .AddApiVersioning(x => {
      x.ApiVersionSelector = new CurrentImplementationApiVersionSelector(x);
      x.AssumeDefaultVersionWhenUnspecified = true;
      x.DefaultApiVersion = new ApiVersion(1, 0);
      x.ReportApiVersions = true;
      x.RouteConstraintName = "version";
    });

  services.AddDbContext<Context>(x =>
    x.UseSqlServer(Configuration.Get<Options>().Database.Connection, y => {
      y.MigrationsHistoryTable("__Migrations");
      y.UseNetTopologySuite();
    }).EnableSensitiveDataLogging(Environment.IsDevelopment())
      .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));

  services.AddRouting(x => { x.AppendTrailingSlash = false; x.LowercaseUrls = true; });

  services.AddCors(x => {
    x.AddPolicy("Cors", y => 
      y.WithOrigins("https://localhost:5000", "https://localhost:5002").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
    });

  services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);

  services
    .AddAuthorization(x => {
      x.AddPolicy(Policy.Admin, y => y.RequireClaim("Admin"));
    });

  services
    .AddIdentityCore<User>(x => x.AddDefaultOptions())
    .AddEntityFrameworkStores<Context>()
    .AddDefaultTokenProviders();

  services.AddOpenIddict()
    .AddValidation(x => {
      x.SetIssuer("https://localhost:5000");
      x.AddAudiences("api");
      x.UseIntrospection();
      x.SetClientId("api").SetClientSecret("test");
      x.UseSystemNetHttp();
      x.UseAspNetCore();
    });

  services.AddHsts();
  services.AddHttpsRedirection();

  services.AddMediatR(typeof(Startup));

  services.Configure<ApiBehaviorOptions>(x => {
    x.SuppressModelStateInvalidFilter = false;
    x.SuppressInferBindingSourcesForParameters = true;
    x.InvalidModelStateResponseFactory = context => new InvalidModelStateResponseFactory(context).GetResponse();
  });

  services.Configure<Options>(Configuration);

}

public void Configure(IApplicationBuilder application, IWebHostEnvironment environment) {

  application.UseHsts();
  application.UseHttpsRedirection();

  application.UseRouting();
  application.UseCors("Cors");

  application.UseAuthentication();
  application.UseAuthorization();

  application.UseEndpoints(x => {
    x.MapControllers();
  });

}

Auth Startup 的 方法是:ConfigureServicesConfigure

public void ConfigureServices(IServiceCollection services) {

  services
    .AddControllersWithViews()
    .SetCompatibilityVersion(CompatibilityVersion.Latest)
    .AddJsonOptions()
    .AddFluentValidation();

  services.AddRouting(x => { x.AppendTrailingSlash = false; x.LowercaseUrls = true; });

  services.AddCors(x => {
    x.AddPolicy("Cors", y => 
      y.WithOrigins("https://localhost:5001", "https://localhost:5002").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
    });

  services.AddDbContext<Context>(x => {

    x.UseSqlServer(Configuration.Get<Options>().Database.Connection, y => {
      y.MigrationsHistoryTable("__Migrations");
      y.UseNetTopologySuite();  
    }).EnableSensitiveDataLogging(Environment.IsDevelopment())
      .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll);

    x.UseOpenIddict<Data.Entities.Application, Authorization, Scope, Token, Int32>();

  });

  services
    .AddIdentityCoreWithAuthentication<User>(x => x.AddDefaultOptions())
    .AddEntityFrameworkStores<Context>()
    .AddDefaultTokenProviders()
    .AddUserConfirmation<UserConfirmation<User>>();

  services.ConfigureApplicationCookie(x => {
    x.AccessDeniedPath = "/denied";
    x.Cookie.HttpOnly = false;
    x.Cookie.Name = "auth";
    x.ExpireTimeSpan = TimeSpan.FromMinutes(40);
    x.LoginPath = "/login";
    x.LogoutPath = "/logout";
    x.SlidingExpiration = true;
  });

  services.AddOpenIddict()
  
    .AddCore(x => {

      x.UseEntityFrameworkCore()
       .UseDbContext<Context>()
       .ReplaceDefaultEntities<Data.Entities.Application, Authorization, Scope, Token, Int32>();
    
    })

    .AddServer(x => {

      x.SetAuthorizationEndpointUris("/connect/authorize")
       .SetLogoutEndpointUris("/connect/logout")
       .SetTokenEndpointUris("/connect/token")
       .SetIntrospectionEndpointUris("/connect/introspect")
       .SetUserinfoEndpointUris("/connect/userinfo");

      x.RegisterClaims(OpenIddictConstants.Claims.Email, OpenIddictConstants.Claims.Name, OpenIddictConstants.Claims.Role);

      x.RegisterScopes(OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Roles, OpenIddictConstants.Scopes.OfflineAccess);
      
      x.AllowAuthorizationCodeFlow()
       .AllowRefreshTokenFlow();

  x.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate();

      x.UseAspNetCore()
       .EnableAuthorizationEndpointPassthrough()
       .EnableLogoutEndpointPassthrough()
       .EnableTokenEndpointPassthrough()
       .EnableUserinfoEndpointPassthrough()
       .EnableStatusCodePagesIntegration();

    })

    .AddValidation(x => {
      x.UseLocalServer();
      x.UseAspNetCore();
    });

  services.AddHsts();
  services.AddHttpsRedirection();

  services.AddMediatR(typeof(Startup));

  services.Configure<Options>(Configuration);

  services.AddScoped<IUserClaimsPrincipalFactory<User>, UserClaimsPrincipalFactory>();

} 

public void Configure(IApplicationBuilder application, IWebHostEnvironment environment) {

  application.UseHsts();
  application.UseHttpsRedirection();

  application.UseStaticFiles();

  application.UseRouting();
  application.UseCors("Cors");

  application.UseAuthentication();
  application.UseAuthorization();

  application.UseEndpoints(x => {
    x.MapDefaultControllerRoute();
  });

}

我究竟做错了什么?

我一直在尝试很多,但总是一样的结果。

标签: angularasp.net-corecorsasp.net-core-5.0openiddict

解决方案


你写的配置应该是正确的。

最初我也有很多关于 CORS 的问题。最后我发现顺序app.UseXXX是必不可少的。要使用 CORS,重要的是在UseCors 之后 UseRouting之前调用 UseAuthenticationUseAuthorization

app.UseRouting(...);
app.UseCors(...);
app.UseAuthentication(...);
app.UseAuthorization(...);

推荐阅读