angular - 由于 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 的 方法是:ConfigureServices
Configure
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();
});
}
我究竟做错了什么?
我一直在尝试很多,但总是一样的结果。
解决方案
你写的配置应该是正确的。
最初我也有很多关于 CORS 的问题。最后我发现顺序app.UseXXX
是必不可少的。要使用 CORS,重要的是在UseCors
之后 UseRouting
和之前调用 UseAuthentication
UseAuthorization
app.UseRouting(...);
app.UseCors(...);
app.UseAuthentication(...);
app.UseAuthorization(...);
推荐阅读
- laravel - Laravel Echo 没有收到来自推送者的事件
- r - 正则表达式在 list.files R 中获取 1 但不是 11(在正则表达式中包含变量)
- swift - 使用 Mac Catalyst 自定义“关于应用程序”窗口
- javascript - 使用模态打开新选项卡并重定向 laravel 项目中的旧页面
- r - 如何将自定义函数中的值导出到 r 中的预定义数据框
- javascript - 使用散景滑块小部件更新绘图数据
- linux - 使用 bash 脚本查找 linux 用户
- ios - (iOS) Obj-c .cer 从字符串存储到 SecCertificateRef
- javascript - 在 Datadog 中,是否有一个 JavaScript 库可以让您获取现有的度量数据?
- javascript - Mongoose group by(发送到客户端后无法设置标头)