由于 CORS 问题,无法访问 API

Posted

技术标签:

【中文标题】由于 CORS 问题,无法访问 API【英文标题】:Not able to access API due to CORS issue 【发布时间】:2021-11-09 21:10:00 【问题描述】:

使用 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 Startup 的ConfigureServicesConfigure 方法是:

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();
  );


我做错了什么?

我尝试了很多,但结果总是一样。

【问题讨论】:

【参考方案1】:

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

最初我也遇到了很多关于 CORS 的问题。最后我发现app.UseXXX 的顺序是必不可少的。要使用 CORS,请务必调用 UseCors after UseRoutingbefore UseAuthenticationUseAuthorization

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

【讨论】:

我在 API 和 AUTH 应用程序中都有这个顺序 确保调用API的应用代码调用的是https,而不是http url?即使您使用的 http-redirection 可能不起作用 是的,我的所有请求都使用了 HTTPS。这真的很奇怪,因为在我登录之前,我可以访问不需要身份验证的端点,例如 https://localhost:5001/news ...登录后,如果我在没有 Authorization 标头的情况下调用该端点,我很好...如果我添加了 Authorisation 标头,但出现 CORS 错误。 您是否在 OpenIdDict 的客户端 Cors Orgins 中列出了您的应用程序 url? @A.Hasemeyer 我不确定我是否理解您的问题。你的意思是,如果我在AddCors 方法中列出了我在https://localhost:5002 上运行的SPA 客户端应用程序?是的,我在里面有AllowAnyOrigin。这是你的意思吗?

以上是关于由于 CORS 问题,无法访问 API的主要内容,如果未能解决你的问题,请参考以下文章

Safari:重新加载后“由于访问控制检查而无法加载 Fetch API”

由于 Spring 5 中的 Access / CORS 问题,无法访问我的后端的 URL [重复]

由于 CORS 问题,无法在 Angular 中发出 HttpClient 发布请求

如何创建代理来访问外部 API? [复制]

无法访问 MVC 6 API

WEB API:我显然无法启用 CORS(?)