IdentityServer 4 2.0 用户信息 "No 'Access-Control-Allow-Origin' header is present on the requested resource."
IdentityServer 4 2.0 userInfo "No 'Access-Control-Allow-Origin' header is present on the requested resource."
场景。
在 运行 本地开发机器上一切正常。但是当 CI 部署到开发服务器时,我们遇到了以下问题。
该站点正确重定向到登录,登录成功后我被重定向回客户端回调页面,该页面更新状态,然后尝试通过 userManager.getUser()
加载用户
这会导致以下情况:
错误信息:
加载失败https://dev-auth.mysite.com/connect/userinfo: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://dev-spa.mysite.com'因此不允许访问。响应具有 HTTP 状态代码 404。
日志
从 Seq Serilog 中我们看到以下其他调用成功。
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration/jwks
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/connect/authorize/callback?client_id=...
请求开始 HTTP/1.1 POST http://dev-auth.mysite.com/Account/Login application/x-www-form-urlencoded 643
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/account/login?returnUrl=...
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/connect/authorize?client_id=...
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration
代码
项目是:
- api.mysite.com
- auth.mysite.com
- spa.mysite.com
所有项目都是.net core 2.0。 spa 项目服务于使用 oidc-client 包的 React 应用程序。
auth.mysite.com Startup.cs
Startup 的身份服务器部分的 CofigureServices 是:
public void ConfigureServices(IServiceCollection aServiceCollection)
{
// CorsPolicy.Any.Apply(aServiceCollection);
aServiceCollection.AddMvc();
//aServiceCollection.AddMvcCore()
// .AddJsonFormatters()
// .AddRazorViewEngine();
aServiceCollection.AddSession();
ConfigureServicesIdentityServer(aServiceCollection);
aServiceCollection.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new ViewLocationExpander());
});
aServiceCollection.AddMediatR();
}
private static void ConfigureServicesIdentityServer(IServiceCollection aServiceCollection)
{
aServiceCollection.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Resources.GetApiResources())
.AddInMemoryClients(Clients.GetClients())
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
// .AddJwtBearerClientAuthentication();
}
配置
public void Configure(
IApplicationBuilder aApplicationBuilder,
IHostingEnvironment aHostingEnvironment,
IOptions<RequestLocalizationOptions> aRequestLocalizationOptions)
{
Environment.UseExceptionPage(aHostingEnvironment, aApplicationBuilder);
// aApplicationBuilder.UseCors(CorsPolicy.Any.DisplayName);
aApplicationBuilder.UseIdentityServer();
aApplicationBuilder.UseRequestLocalization(aRequestLocalizationOptions.Value);
aApplicationBuilder.UseStaticFiles(); // Serve up static files from wwwroot images etc...
aApplicationBuilder.UseSession();
aApplicationBuilder.UseMvc();
}
客户端配置:
new Client
{
ClientId = "someGUID",
ClientName = "MySite",
RequireClientSecret=false, // if false this is a public client.
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = {
"http://local-spa.mysite.com:3000/callback",
"https://dev-spa.mysite.com/callback",
},
PostLogoutRedirectUris = {
"http://local-spa.mysite.com:3000/",
"https://dev-spa.mysite.com/",
},
AllowedCorsOrigins = {
"http://local-spa.mysite.com:3000",
"https://dev-spa.mysite.com",
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
Resources.WebOrderingApi,
},
RequireConsent = false,
},
TypeScript 客户端:
import { Log, OidcClientSettings, UserManager, UserManagerSettings, } from 'oidc-client';
...
const protocol: string = window.location.protocol;
const hostname: string = window.location.hostname;
const port: string = window.location.port;
const oidcClientSettings: OidcClientSettings = {
client_id: '46a0ab4a-1321-4d77-abe5-98f09310df0b',
post_logout_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/`,
redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
response_type: 'id_token token',
scope: 'openid profile email phone WebOrderingApi',
};
const userManagerSettings: UserManagerSettings = {
...oidcClientSettings,
automaticSilentRenew: false,
filterProtocolClaims: true,
loadUserInfo: true,
monitorSession: false,
silent_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
};
更新
原来是开发IIS服务器不允许OPTIONS动词。这是在不需要 CORS 的生产服务器中推荐的做法,但我不建议在开发环境中这样做。
感谢大家的贡献。
此问题的一个修复方法是允许 IIS 中的 OPTIONS 动词用于身份验证服务器。
为什么这不起作用
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://dev-spa.mysite.com' is therefore not allowed access. The response had HTTP status code 404.
在跨源发出请求之前,会发出 CORS 请求,以检查是否应允许该请求,这是使用 OPTIONS 方法发送的,您的服务器以 404 响应,停止了请求
如何修复
您需要发回有效的 OPTIONS 响应,以允许请求。
我建议使用 Wireshark 或其他一些检查器来查看发送的请求及其响应,以帮助调试 CORS 响应停止请求的原因。
正如@Kirk 提到的,问题可能出在 HTTPS 上,要么是 CORS 请求,要么是整个服务器不支持 HTTPS。
资源
根据您的错误消息,我在最后注意到以下内容:
The response had HTTP status code 404.
这表明您的问题实际上可能不是 CORS 问题 - 可能更多是找不到您尝试访问的端点,从而导致 404 响应。当然,这个404响应不太可能包含相关的Access-Control-Allow-Origin
header.
查看您的日志,所有对 dev-auth.mysite.com
的成功调用都使用 http
方案。但是,在引用 connect/userinfo
端点时,您的错误消息指的是 https
方案:
Failed to load https://dev-auth.mysite.com/connect/userinfo
根据您可以混合在 .NET Core CORS 中的文档,IdentityServer 会尊重它。在您的 Startup.cs 中,添加:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddCors(options =>
{
options.AddPolicy("WhateverNameYouWantHere",
builder =>
{
builder.WithOrigins($"http://{_frontendUri}",
$"https://{_frontendUri}")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
...
}
然后
public void Configure(IApplicationBuilder app)
{
...
app.UseCors("WhateverNameYouWantHere");
}
场景。
在 运行 本地开发机器上一切正常。但是当 CI 部署到开发服务器时,我们遇到了以下问题。
该站点正确重定向到登录,登录成功后我被重定向回客户端回调页面,该页面更新状态,然后尝试通过 userManager.getUser()
加载用户
这会导致以下情况:
错误信息:
加载失败https://dev-auth.mysite.com/connect/userinfo: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://dev-spa.mysite.com'因此不允许访问。响应具有 HTTP 状态代码 404。
日志
从 Seq Serilog 中我们看到以下其他调用成功。
请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration/jwks 请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration 请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/connect/authorize/callback?client_id=... 请求开始 HTTP/1.1 POST http://dev-auth.mysite.com/Account/Login application/x-www-form-urlencoded 643 请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/account/login?returnUrl=... 请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/connect/authorize?client_id=... 请求开始 HTTP/1.1 GET http://dev-auth.mysite.com/.well-known/openid-configuration
代码
项目是:
- api.mysite.com
- auth.mysite.com
- spa.mysite.com
所有项目都是.net core 2.0。 spa 项目服务于使用 oidc-client 包的 React 应用程序。
auth.mysite.com Startup.cs
Startup 的身份服务器部分的 CofigureServices 是:
public void ConfigureServices(IServiceCollection aServiceCollection)
{
// CorsPolicy.Any.Apply(aServiceCollection);
aServiceCollection.AddMvc();
//aServiceCollection.AddMvcCore()
// .AddJsonFormatters()
// .AddRazorViewEngine();
aServiceCollection.AddSession();
ConfigureServicesIdentityServer(aServiceCollection);
aServiceCollection.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new ViewLocationExpander());
});
aServiceCollection.AddMediatR();
}
private static void ConfigureServicesIdentityServer(IServiceCollection aServiceCollection)
{
aServiceCollection.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Resources.GetApiResources())
.AddInMemoryClients(Clients.GetClients())
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
// .AddJwtBearerClientAuthentication();
}
配置
public void Configure(
IApplicationBuilder aApplicationBuilder,
IHostingEnvironment aHostingEnvironment,
IOptions<RequestLocalizationOptions> aRequestLocalizationOptions)
{
Environment.UseExceptionPage(aHostingEnvironment, aApplicationBuilder);
// aApplicationBuilder.UseCors(CorsPolicy.Any.DisplayName);
aApplicationBuilder.UseIdentityServer();
aApplicationBuilder.UseRequestLocalization(aRequestLocalizationOptions.Value);
aApplicationBuilder.UseStaticFiles(); // Serve up static files from wwwroot images etc...
aApplicationBuilder.UseSession();
aApplicationBuilder.UseMvc();
}
客户端配置:
new Client
{
ClientId = "someGUID",
ClientName = "MySite",
RequireClientSecret=false, // if false this is a public client.
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = {
"http://local-spa.mysite.com:3000/callback",
"https://dev-spa.mysite.com/callback",
},
PostLogoutRedirectUris = {
"http://local-spa.mysite.com:3000/",
"https://dev-spa.mysite.com/",
},
AllowedCorsOrigins = {
"http://local-spa.mysite.com:3000",
"https://dev-spa.mysite.com",
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
Resources.WebOrderingApi,
},
RequireConsent = false,
},
TypeScript 客户端:
import { Log, OidcClientSettings, UserManager, UserManagerSettings, } from 'oidc-client';
...
const protocol: string = window.location.protocol;
const hostname: string = window.location.hostname;
const port: string = window.location.port;
const oidcClientSettings: OidcClientSettings = {
client_id: '46a0ab4a-1321-4d77-abe5-98f09310df0b',
post_logout_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/`,
redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
response_type: 'id_token token',
scope: 'openid profile email phone WebOrderingApi',
};
const userManagerSettings: UserManagerSettings = {
...oidcClientSettings,
automaticSilentRenew: false,
filterProtocolClaims: true,
loadUserInfo: true,
monitorSession: false,
silent_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
};
更新
原来是开发IIS服务器不允许OPTIONS动词。这是在不需要 CORS 的生产服务器中推荐的做法,但我不建议在开发环境中这样做。
感谢大家的贡献。
此问题的一个修复方法是允许 IIS 中的 OPTIONS 动词用于身份验证服务器。
为什么这不起作用
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://dev-spa.mysite.com' is therefore not allowed access. The response had HTTP status code 404.
在跨源发出请求之前,会发出 CORS 请求,以检查是否应允许该请求,这是使用 OPTIONS 方法发送的,您的服务器以 404 响应,停止了请求
如何修复
您需要发回有效的 OPTIONS 响应,以允许请求。
我建议使用 Wireshark 或其他一些检查器来查看发送的请求及其响应,以帮助调试 CORS 响应停止请求的原因。
正如@Kirk 提到的
资源
根据您的错误消息,我在最后注意到以下内容:
The response had HTTP status code 404.
这表明您的问题实际上可能不是 CORS 问题 - 可能更多是找不到您尝试访问的端点,从而导致 404 响应。当然,这个404响应不太可能包含相关的Access-Control-Allow-Origin
header.
查看您的日志,所有对 dev-auth.mysite.com
的成功调用都使用 http
方案。但是,在引用 connect/userinfo
端点时,您的错误消息指的是 https
方案:
Failed to load https://dev-auth.mysite.com/connect/userinfo
根据您可以混合在 .NET Core CORS 中的文档,IdentityServer 会尊重它。在您的 Startup.cs 中,添加:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddCors(options =>
{
options.AddPolicy("WhateverNameYouWantHere",
builder =>
{
builder.WithOrigins($"http://{_frontendUri}",
$"https://{_frontendUri}")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
...
}
然后
public void Configure(IApplicationBuilder app)
{
...
app.UseCors("WhateverNameYouWantHere");
}