【发布时间】:2021-12-12 05:29:39
【问题描述】:
这个项目仍在开发中,可以在我的开发笔记本电脑上完美运行,但是,当我发布到我的共享托管 Web 服务器 (Winserve) 时,功能就会中断。
旁注:我确信这在几周前运行良好,但我可能错了,因为我大部分时间都在我的开发笔记本电脑上运行它。
我有一个基本的 asp.net Core 3.1 Web 应用程序。我在 startup.cs 文件的 Configure() 和 ConfigureServices() 部分中添加了 Cookie 和身份验证信息。
我可以在 PROD 中登录应用程序,并且我的身份验证 cookie 似乎设置正确,它正确地从数据库中获取角色并添加相关声明等,并为用户提供正确的角色。 我知道这一点是因为 UI 会根据 User.Identity.IsAuthenticated = true 以及 User.IsInRole("Some Role") 等是否发生变化。
但是,在(不一致的)时间段(通常大约 30-40 秒)之后,当我导航到需要对用户进行身份验证(使用 [Authorize] 限定)的页面(控制器/操作)时,我得到重定向回登录页面!不过,这不仅仅是重定向,用户要么已注销,要么 cookie 不再有效。我每 5 秒尝试一次,持续了大约 30 秒,之后碰巧回到“授权”网址,每次我被推回登录页面。
在这段时间内(30 到 40 秒之前),我可以导航到安全页面,让我心满意足。所有不同的页面,或者一个接一个,或者在导航之间留几秒钟的间隙,它仍然有效,直到它不起作用!
另外,我已经在浏览器检查器中检查了 Cookie,并且肯定会创建 Cookie,我认为它的默认到期日期约为 14 天。但无论如何,这都是未来的方式。
这是我的 Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddRazorRuntimeCompilation().AddNewtonsoftJson();
services.AddControllers().AddNewtonsoftJson();
services.AddDistributedMemoryCache();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
});
services.AddAuthorization();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddMvc().AddNewtonsoftJson();
services.AddRazorPages().AddNewtonsoftJson();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
这是我的 HomeController 中的登录和注销方法...
public IActionResult Login(LoginViewModel model)
{
if (string.IsNullOrEmpty(model.Username) || string.IsNullOrEmpty(model.Password)) { model.Errors.Add("Must provide a Username & Password"); return View(model); }
var _user = _authenticationBusinessService.AuthenticateUser(model.Username, model.Password);
if (_user != null && _user.Id > 0) { return SignUserIn(model, _user); }
else { model.Errors.Add("We're unable to authenticate you with the credentials provided"); }
return View(model);
}
public IActionResult SignOut()
{
return SignUserOut();
}
还有他们调用的方法……
private IActionResult SignUserIn(LoginViewModel model, UserDTO user)
{
var _claims = new List<Claim>
{
//User identity information
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Id.ToString()),
new Claim("FirstName", user.Person.FirstName),
new Claim("Surname", user.Person.Surname)
};
//Roles/Permissions
_claims.AddRange(user.UserPermissionMaps.Select(x => new Claim(ClaimTypes.Role, x.Permission.Description)));
var _claimsIdenity = new ClaimsIdentity(_claims, CookieAuthenticationDefaults.AuthenticationScheme);
var _authProperties = new AuthenticationProperties() { IsPersistent = true, AllowRefresh = true };
var _claimsPrincipal = new ClaimsPrincipal(_claimsIdenity);
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, _claimsPrincipal, _authProperties);
if (string.IsNullOrEmpty(model.ReturnURL))
{
model.ReturnURL = "/";
}
return Redirect(model.ReturnURL);
}
private IActionResult SignUserOut()
{
HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
HttpContext.Session.Clear();
return RedirectToAction("Index");
}
您需要获得授权才能访问的示例 Cotroller...
[Area("Admin")]
public class HomeController : Controller
{
[Authorize]
public IActionResult Index()
{
return View();
}
}
【问题讨论】:
-
我没有想到任何理论,但我发现在调试此类身份验证问题时有用的一件事是在 Microsoft.AspNetCore.Authentication 上下文中启用详细日志记录。您如何做到这一点取决于您的日志记录提供商。
-
您的共享主机多久回收一次您的应用程序?您可以检查几件事。如果在内存会话/cookie 存储中使用,请检查它是否可以在回收利用中幸存下来。检查您是否将数据保护管道的密钥环存放在可以回收利用的地方。
-
@weichch 感谢您的意见。您是否暗示它可能与应用程序池有关?这不是会话而不是 Cookie 吗?我已经问过我的主人关于它多久回收一次的问题。只等回复。
-
@JackA。谢谢,我会看看我能用日志做些什么。我只是使用这些 .net 核心 Web 应用程序模板的默认记录器... public HomeController(ILogger
logger) -
它可以是应用程序池或数据保护管道。如果您没有使用 cookie 存储,那么您的 cookie 将包含完整的身份验证票证,在这种情况下,它可以在应用程序池回收后继续存在。但是,cookie 在发送回客户端之前使用数据保护管道加密,管道必须维护密钥环并将其保存在永久位置,否则如果您稍后将其发送回,它无法解密任何 cookie。如果是这种情况,用户将未经授权并受到质疑。
标签: c# .net asp.net-core .net-core cookies