提问人:jacklohse 提问时间:11/3/2023 更新时间:11/3/2023 访问量:26
ASP.NET 身份 Cookie 未在具有不同端口的 localhost 上设置
ASP.NET Identity Cookie not being set on localhost with different ports
问:
我有两个应用程序(带有 Angular 的 .NET 7),都在 localhost 上,但端口不同
应用 1:这是一个共享的 Web API,它充当我的其他应用程序的单一登录点。
应用程序 2:这是主应用程序,但它调用应用程序 1 的 API 进行登录。
这两个应用都使用 Visual Studio 提供的带有 Angular 模板的 ASP.NET Core。
目标:用户可以进入应用程序 2 并通过输入他们的用户名和密码来发起登录请求。单击登录后,这些凭据将发送到应用程序 1 以处理身份验证,然后应用程序 1 发送身份验证 cookie
我的问题:从应用程序 2 到应用程序 1 的登录请求有效,用户数据在应用程序 2 中返回,并发送身份验证 cookie。但是,Set-Cookie 标头实际上并没有将 cookie 保存到浏览器,特别是针对 localhost,因此用户不会进行身份验证。Postman 中的相同请求有效,并且 cookie 将保存到两个应用程序,并且用户会进行身份验证。
我认为正在发生的事情:我认为这可能与 chrome 处理特殊域 localhost 的方式有关。我可以得到这个完全相同的请求,在没有缺陷的情况下在 Postman 上工作。这些应用位于不同的端口上:应用 1:https://localhost:44465,应用 2:https://localhost:5000。我在网上阅读了相当多的文章,说因为它们位于不同的端口上,所以 chrome 会阻止 cookie 被共享。
APP 1 已编辑程序 .cs
var builder = WebApplication.CreateBuilder(args);
string? envRaw = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
string env = envRaw is null ? "Development" : envRaw;
var Config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env}.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables().Build();
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("SHARED DIRECTORY"))
.SetApplicationName("APP_NAME")
.SetDefaultKeyLifetime(TimeSpan.FromDays(7));
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
}).AddIdentityCookies();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "APP_NAME";
options.Cookie.Path = "/";
options.Cookie.Domain = null;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.None;
options.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = ctx =>
{
var isApiCall = ctx.Request.Path.StartsWithSegments("/api");
var hasOkStatus = ctx.Response.StatusCode == (int)HttpStatusCode.OK;
if (isApiCall && hasOkStatus)
{
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.FromResult<object>(null);
},
// Do not redirect to /AccessDenied for forbidden API call; return Forbidden status code instead.
OnRedirectToAccessDenied = ctx =>
{
var isApiCall = ctx.Request.Path.StartsWithSegments("/api");
var hasOkStatus = ctx.Response.StatusCode == (int)HttpStatusCode.OK;
if (isApiCall && hasOkStatus)
{
ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.FromResult<object>(null);
}
};
});
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}"
).RequireCors("CorsPolicy");
app.MapDefaultControllerRoute();
app.MapFallbackToFile("index.html");
app.Run();
APP 2 已编辑程序 .cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
options.AddPolicy("CorsPolicy", builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
)
);
// ASP.Net Autentication
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("SHARED DIRECTORY"))
.SetApplicationName("APP_NAME")
.SetDefaultKeyLifetime(TimeSpan.FromDays(7));
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
}).AddIdentityCookies();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "APP_NAME";
options.Cookie.Path = "/";
options.Cookie.Domain = null;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.None;
options.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = ctx =>
{
var isApiCall = ctx.Request.Path.StartsWithSegments("/api");
var hasOkStatus = ctx.Response.StatusCode == (int)HttpStatusCode.OK;
if (isApiCall && hasOkStatus)
{
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.FromResult<object>(null);
},
OnRedirectToAccessDenied = ctx =>
{
var isApiCall = ctx.Request.Path.StartsWithSegments("/api");
var hasOkStatus = ctx.Response.StatusCode == (int)HttpStatusCode.OK;
if (isApiCall && hasOkStatus)
{
ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.FromResult<object>(null);
}
};
});
var app = builder.Build();
var cookiePathBase = builder.Configuration["App:CookiePathBase"];
app.UsePathBase(cookiePathBase);
app.Use((context, next) =>
{
context.Request.PathBase = new PathString(cookiePathBase);
return next();
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");
app.Run();
在此处
输入图像描述 从 app2 到 app1 的请求返回 200 OK 的图片 在此处输入图像描述 200 OK
响应
中包含的身份验证 cookie 的图片 在此处
输入图像描述 在 Postman 中正确设置 Cookie
我几乎尝试了网上能找到的所有东西。所以这里是我尝试过的事情的清单,但没有一个改变任何东西:
将 Cookie 域指定为 “localhost”、null、“”,并完全省略它
我将 cookie 选项设置为 SameSite=None,并将安全策略设置为“一如既往”。我还尝试了宽松和严格的 SameSite 模式
我尝试使用 proxy.conf.js 将对 localhost:44465 的请求代理为 localhost:5000,希望这会绕过跨站点 cookie
将 Access-Control-Allow-Origin 设置为特定端口,而不是通配符值 (*)
将 Access-Control-Allow-Credentials 设置为 true
将 Cookie 设置为 HttpOnly,并为其指定会话以外的到期日期
将每个应用程序中的 Cookie 名称更改为唯一
尝试过允许 CORS 的 Chrome 扩展程序
这些都没有产生任何结果。我的下一个尝试是将 IIS 主机配置为具有 localhost 的子域来镜像生产环境,例如。app1.localhost 和 app2.localhost。
期待听到你们的建议!
答: 暂无答案
评论