在localhost上一切正常。
一旦部署到AWS Lambda,我注意到的第一个问题是返回URL缺少以正斜杠开头的“ connect / authorize / ...”而不是“ / connect / authorize”。 这是完整的网址“帐户/登录?ReturnUrl = connect%2Fauthorize%2Fcallback%3Fclient_id%3D89F3BD2C-C7FE-443C-B5AA-20241BB8A728%26nonce%3DyrOeuRsqk22SyZidU7gFPwydULf5ygpXH %%% F%_%2 %% F%_3 %% 26%A2 %% 2 %% 2%F3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D 2520token%26scope%3Dopenid%2520profile%2520offline_access%26state%3DyrOeuRsqk22SyZidU7gFPwydULf5ygpXH9nSGPUj“
所以我必须在代码中手动添加正斜杠。那么我可以看到用户已成功通过身份验证。在本地主机上,在响应标题中,我将获得两个set-cookie
Set-Cookie:idsrv.session = sK-Bo4lSJ3CS1289AC5aXw;路径= /; samesite = none
的Set-Cookie:idsrv = CfDJ8B3Fm0L2YmhJjEx10Gqzt669qjVDxEbr_Gi8Myuwq2YLZBBpQMzbwDK9ItkNMDWbXq46rXRsfC8FmXs28oFcIfhLpWg-6Yzyx1AutK5SoOvaZlvI3Y0Hmus0LKOXJCXXdBtiT1dlzr6BbEwH5TWPRhzRmfXGZRCDV2qS-azGMRfuXl-B-bPq_2Bb9a29VWw9rUMAXioXrhXq83BEEngi7UuAOZPfPu88QqTk55m4T4qJYD0X9lCJdiC33Z3IbmVq88TEYmDlzXvE2MGeloyVMg-GZzZj8JuPNuH6-qMLon55zBG4G-cEFh77lWkT3oGDu9sV6zBrSihljkfV87iMV7lQz0lTG3Db6fvqGJxQPmEOmX7-HBc_8jV5Jh5jznYckHyfgdIPaVrgG1dGe68FEyO1L0XkMCWNhZhcYET6IMAISmMAXKUXX6E3C90uSANo5VgumUe5qofdNzHBmloL9uc9fmpaWleMZg1SNPFI27SjQo2B4ZrDHDH5-R2uAqzRAQ;路径= /; samesite = none; httponly
但是在服务器上,我只有第一个
set-cookie:idsrv.session = GAAHJhReZicjWjNcMaN2eg;路径= /;安全; samesite = none
没有将第二个set-cookie传递回“ / connect / authorize”,它只是重定向回到登录页面。 我已经将samesite设置为none。 ID服务器位于https上,并且应该是安全的。
下面是代码
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Logout";
options.Authentication = new AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
CookieSlidingExpiration = true
};
})
.AddInMemoryIdentityResources(Config.Ids);
builder.Services.ConfigureExternalCookie(options => {
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1); //SameSiteMode.Unspecified in .NET Core 3.1
});
builder.Services.ConfigureApplicationCookie(options => {
options.Cookie.IsEssential = true;
options.Cookie.SameSite = (SameSiteMode)(-1); //SameSiteMode.Unspecified in .NET Core 3.1
});
if (_environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
var cert = new X509Certificate2(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Cert", Configuration.GetSection("AppSettings")["CertificateName"]),
Configuration.GetSection("AppSettings")["CertificatePassword"]);
builder.AddSigningCredential(cert);
}
services.AddTransient<IProfileService, ProfileService>();
services.AddTransient<IClientStore, ClientService>();
services.AddTransient<ICorsPolicyService, CorsService>();
services.AddTransient<IPersistedGrantStore, PersistedGrantService>();
services.AddTransient<IEventSink, SeqEventService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
//launched in debug build from VS2017
#if DEBUG
app.UseDeveloperExceptionPage();
#endif
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseExceptionHandler("/Error");
app.UseHsts();
app.UseStartUpWarmer<IEntityConfigurationApi<FilePurposeData>>(api =>
{
api.ListIdValueModels(new IdValueRequest()
{
ValuePropertyName = "Name"
});
});
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endPoints =>
{
endPoints.MapControllerRoute("default", "{controller=Home}/{action=Index}");
});
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(opt =>
{
opt.RouteTemplate = "Help/swagger/{documentName}/swagger.json";
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/Help/swagger/v1/swagger.json", $"/{GetType().Namespace}");
c.RoutePrefix = "Help";
});
}
在AccountController中
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
// the user clicked the "cancel" button
if (model.Button != "login")
{
if (context != null)
{
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);
//// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (await _clientStore.IsPkceClientAsync(context.ClientId))
{
// if the client is PKCE then we assume it's native, so this change in how to
// return the response is for better UX for the end user.
return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl });
}
return Redirect(model.ReturnUrl);
}
else
{
// since we don't have a valid context, then we just go back to the home page
return Redirect("~/");
}
}
var errorMessage = string.Empty;
if (ModelState.IsValid)
{
if (model.ReturnUrl.ToLower().StartsWith("connect/authorize"))
{
model.ReturnUrl = "/" + model.ReturnUrl;
}
try
{
var authResult = DoInService(svc =>
{
return svc.AuthenticateUser(new AuthenticateUserRequest
{
LoginName = model.Username,
Password = model.Password
});
});
if (authResult?.IsAuthenticated == true)
{
var lst = authResult.Claims
.Where(cl => cl.Type != "Organization")
.ToList();
var userId = lst.First(f => f.Type == "Payliance.UserIdentifier").Value;
await _events.RaiseAsync(new UserLoginSuccessEvent(model.Username, userId, model.Username, clientId: context?.ClientId));
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
// issue authentication cookie with subject ID and username
await HttpContext.SignInAsync(userId, model.Username, props);
Logger.LogItem("99999 - Authentication Cookie issued");
Logger.LogItem($"99999 - ReturnURL: {model.ReturnUrl}");
if (context != null)
{
Logger.LogItem("99999 - Redirect with context");
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(model.ReturnUrl);
}
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
Logger.LogItem("99999 - Redirect without context");
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
errorMessage = "invalid return URL";
}
}
else
{
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.ClientId));
errorMessage = "Invalid login. Check your username/password and try again. \nIf you need further assistance, please contact us at 800-634-4484.";
}
}
catch (Exception e)
{
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.ClientId));
errorMessage =
"Login problems. Please try again. \nIf you need further assistance, please contact us at 800-634-4484.";
}
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
vm.ErrorMessage = errorMessage;
return View(vm);
}