我采用IdentityServer4示例代码(Combined_AspId_and_EFStorage)
,并参考了晓晨的博客(IdentityServer4(10)- 添加对外部认证的支持之QQ登录)。
在第三方登录时开始都没问题,能够跳转到QQ授权页面并成功返回QQ用户信息,但是在跳转回MvcClient时却得到404空白页面:
404 Not Found
identity服务器的startup.cs代码如下:
public void ConfigureServices(IServiceCollection services)
{
Services = services;
var connectionString = Configuration.GetConnectionString("qcloud-postgres-applicationdb");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(connectionString));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_2);
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseNpgsql(Configuration.GetConnectionString("qcloud-postgres-configurationdb"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseNpgsql(Configuration.GetConnectionString("qcloud-postgres-persistedgrantdb"),
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 60;
})
.AddAspNetIdentity<ApplicationUser>();
builder.AddDeveloperSigningCredential();
/*
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}*/
services.AddAuthentication(
options =>
{
options.DefaultScheme = "QQ";
})
.AddQQ(qqOptions =>
{
qqOptions.AppId = "AppId123456"; //
qqOptions.AppKey = "AppKey1234567890";
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
MvcClient端的startup.cs代码如下:
public void ConfigureServices(IServiceCollection services)
{
Services = services;
Services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
//options.Authority = "http://localhost:5000";
options.Authority = "https://identity.web123456.cn";
options.RequireHttpsMetadata = false;
options.ClientId = "yingyu88";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("yingyu88api");
//options.Scope.Add("offline_access");
//options.ClaimActions.MapJsonKey("website", "website");
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
客户端得到的log如下:
2019-03-23 11:36:07.588 +08:00 [INF] User profile not available. Using 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\S-1-5-82-1592072215-1740167268-757123690-3585059337-856257778\DataProtection' as key repository and Windows DPAPI to encrypt keys at rest.
2019-03-23 11:36:08.070 +08:00 [INF] Request starting HTTP/1.0 GET http://www.yingyu88.cn/Home/Secure
2019-03-23 11:36:08.187 +08:00 [INF] Route matched with {action = "Secure", controller = "Home"}. Executing action MvcClient.Controllers.HomeController.Secure (Yingyu88web)
2019-03-23 11:36:08.197 +08:00 [INF] Authorization failed.
2019-03-23 11:36:08.202 +08:00 [INF] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
2019-03-23 11:36:08.209 +08:00 [INF] Executing ChallengeResult with authentication schemes ([]).
2019-03-23 11:36:08.713 +08:00 [INF] AuthenticationScheme: oidc was challenged.
2019-03-23 11:36:08.722 +08:00 [INF] Executed action MvcClient.Controllers.HomeController.Secure (Yingyu88web) in 531.87060000000008ms
2019-03-23 11:36:08.742 +08:00 [INF] Request finished in 673.8004ms 302
2019-03-23 11:36:15.048 +08:00 [INF] Request starting HTTP/1.0 POST http://www.yingyu88.cn/signin-oidc application/x-www-form-urlencoded 1532
2019-03-23 11:36:15.418 +08:00 [INF] AuthenticationScheme: Cookies signed in.
2019-03-23 11:36:15.419 +08:00 [INF] Request finished in 370.649ms 302
从以上Log可以看出Signin是成功的,但是callback回到signin-oidc之后请求就结束了,诡异的是应用并没有报错。
如有哪位大神熟悉IdentityServer的请多多赐教!
如果需要我这边更多的代码和log信息可以加QQ:352862120联系我私聊。多谢!
2019-03-29更新:
经过试验发现,IdentityServer4的示例代码在调试状态下没有问题,能够在localhost:5000和localhost:5002之间跳转并传递相应的Cookies。但是部署到服务器后(我用的是windows 2016和iis)就不行了(代码完全一致)。最初直接想到的是跨域问题,但是加上AddCors也并没有解决。
public void ConfigureServices(IServiceCollection services)
{
//配置跨域处理,允许所有来源:
services.AddCors(options =>
options.AddPolicy("corspolicy",
p => p.AllowAnyOrigin())
);
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
** app.UseCors("corspolicy");
** app.UseMvcWithDefaultRoute();
}
以上代码加到了IdentityServer和MvcClient,但是并没有效果。
追踪部署环境和调试环境下的log可以发现,公网部署时IdentityServer运行到signin-oidc就停止了,没有任何报错。
这是公网部署时的log:
2019-03-25 13:46:19.030 +08:00 [INF] Request starting HTTP/1.0 GET http://www.yingyu88.cn/Home/Secure
2019-03-25 13:46:19.031 +08:00 [INF] Route matched with {action = "Secure", controller = "Home"}. Executing action MvcClient.Controllers.HomeController.Secure (MvcClient)
2019-03-25 13:46:19.039 +08:00 [INF] Authorization failed.
2019-03-25 13:46:19.041 +08:00 [INF] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
2019-03-25 13:46:19.045 +08:00 [INF] Executing ChallengeResult with authentication schemes ([]).
2019-03-25 13:46:19.403 +08:00 [INF] AuthenticationScheme: oidc was challenged.
2019-03-25 13:46:19.408 +08:00 [INF] Executed action MvcClient.Controllers.HomeController.Secure (MvcClient) in 376.51370000000003ms
2019-03-25 13:46:19.413 +08:00 [INF] Request finished in 382.7936ms 302
2019-03-25 13:46:40.244 +08:00 [INF] Request starting HTTP/1.0 POST http://www.yingyu88.cn/signin-oidc application/x-www-form-urlencoded 1531
2019-03-25 13:46:40.651 +08:00 [INF] AuthenticationScheme: Cookies signed in.
2019-03-25 13:46:40.651 +08:00 [INF] Request finished in 406.993ms 302
这是调试环境下的log:
2019-03-25 12:32:15.062 +08:00 [INF] Request starting HTTP/1.1 GET http://localhost:5002/Home/Secure
2019-03-25 12:32:15.064 +08:00 [INF] Route matched with {action = "Secure", controller = "Home"}. Executing action MvcClient.Controllers.HomeController.Secure (MvcClient)
2019-03-25 12:32:15.070 +08:00 [INF] Authorization failed.
2019-03-25 12:32:15.072 +08:00 [INF] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
2019-03-25 12:32:15.073 +08:00 [INF] Executing ChallengeResult with authentication schemes ([]).
2019-03-25 12:32:15.077 +08:00 [INF] AuthenticationScheme: oidc was challenged.
2019-03-25 12:32:15.077 +08:00 [INF] Executed action MvcClient.Controllers.HomeController.Secure (MvcClient) in 13.078700000000001ms
2019-03-25 12:32:15.077 +08:00 [INF] Request finished in 14.6766ms 302
2019-03-25 12:32:39.168 +08:00 [INF] Request starting HTTP/1.1 POST http://localhost:5002/signin-oidc application/x-www-form-urlencoded 1522
2019-03-25 12:32:39.543 +08:00 [INF] AuthenticationScheme: Cookies signed in.
2019-03-25 12:32:39.543 +08:00 [INF] Request finished in 375.4138ms 302
//以下是调试环境下继续执行跳转回/home/secure的动作。部署后这些动作就没有被执行。
2019-03-25 12:32:39.550 +08:00 [INF] Request starting HTTP/1.1 GET http://localhost:5002/Home/Secure
2019-03-25 12:32:39.551 +08:00 [INF] Route matched with {action = "Secure", controller = "Home"}. Executing action MvcClient.Controllers.HomeController.Secure (MvcClient)
2019-03-25 12:32:39.551 +08:00 [INF] Authorization was successful.
2019-03-25 12:32:39.551 +08:00 [INF] Executing action method MvcClient.Controllers.HomeController.Secure (MvcClient) - Validation state: "Valid"
2019-03-25 12:32:39.552 +08:00 [INF] Executed action method MvcClient.Controllers.HomeController.Secure (MvcClient), returned result Microsoft.AspNetCore.Mvc.ViewResult in 0.2637ms.
2019-03-25 12:32:39.556 +08:00 [INF] Executing ViewResult, running view Secure.
2019-03-25 12:32:39.562 +08:00 [INF] Executed ViewResult - view Secure executed in 10.0428ms.
2019-03-25 12:32:39.563 +08:00 [INF] Executed action MvcClient.Controllers.HomeController.Secure (MvcClient) in 11.7323ms
2019-03-25 12:32:39.565 +08:00 [INF] Request finished in 14.3843ms 200 text/html; charset=utf-8
观察对比两种环境下的cookie,发现部署后从IdentityServer跳转到MvcClient/signin-oidc后少了两个cookie:
.AspNetCore.Identity.Application
idsrv.session
从IdentityServer4官方的示例代码来看,其示例代码可能还无法直接应用于部署环境,在startup.cs中有一段代码:
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
由此可见sigin-oidc的404问题有可能是解密凭据引起的。但是我对ASP.net core源码了解太少,提供上述信息希望能够得到大神的指点。
2019-03-29更新:AddDeveloperSigningCredential会生成RSA加密证书,使得程序能够正常运行,虽然其安全性会比较差。详见IdentityServer4部署到服务器,配置证书问题。因此AddDeveloperSigningCredential并非导致本次问题的原因。
2019-03-26更新:
如果把IdentityServer4.Samples\Quickstarts\4_ImplicitFlowAuthenticationWithExternal示例部署到同样的环境,发现能够运行通过。但是如果改为Hybrid模式就会出现signin-oidc 404 Not Found的问题。
identityserver.config.cs代码:
// OpenID Connect hybrid flow client (MVC)
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
// 能正常部署运行:
AllowedGrantTypes = GrantTypes.Implicit,
// 会出现404问题:
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets = { new Secret("secret".Sha256()) },
RedirectUris = { "http://www.someweb.cn/signin-oidc" },
PostLogoutRedirectUris = { "http://www.someweb.cn/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
MvcClient.startup.cs:
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "https://identity.someweb.cn";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("offline_access");
options.ClaimActions.MapJsonKey("website", "website");
// 能够正常部署运行:
options.ResponseType = "id_token";
// 会发生signin-oidc 404错误:
options.Scope.Add("api1");
options.ResponseType = "code id_token";
// 会发生"invalid_scope" 错误:
options.Scope.Add("api1");
options.ResponseType = "id_token";
});
现在虽然已经知道Hybrid模式下公网部署才会出现错误,但究竟是我对client的配置错误还是IS4的bug所引起的问题并不清楚。
我试过AddCors,但并没有解决以上问题。