OWIN 身份验证在本地工作正常但在开发环境中不工作
OWIN Authentication works fine locally but not on dev environment
我遇到了一些奇怪的问题。
我在弹出窗口中有一个登录表单。提交表单时 AJAX 向服务器发送请求,以加密的登录名和密码以及防伪令牌作为参数。控制器方法被调用,这个方法检查用户输入并且可以 return:
a) 如果用户输入不正确,则 HTML 对于部分视图(登录表单)有验证错误,注入而不是现有的登录表单
b) 如果用户输入正确,则刷新页面并为经过身份验证的用户重新显示。
我正在使用 OWIN 对用户进行身份验证。
Javascript - AJAX 调用
"use strict";(function ($, undefined) {
$(document).ready(function () {
$(document).on('submit', 'div.modal-dialog form', function () {
let $form = $(this),
//token = $('input[name="__RequestVerificationToken"]', form).val(),
logindata = $form.serialize(),
$formParent = $form.parent();
$.ajax({
url: $form.attr('action'),
type: 'POST',//form.attr('method'),
data: {
mdata: b64Encode(logindata),
},
success: function (result) {
if (result.resultsuccess) {
location.reload(true);
}
let $resultHtml = $('<div/>').html(result).contents();
let mbody = $resultHtml.find('div.modal-body');
if (mbody.length) {
$formParent.children().remove();
$formParent.html($resultHtml.find('div.modal-body').children());
}
},
error: function (result) {
console.log(result);
}
});
return false;
});
function GetAntiForgeryToken() {
let tokenField = $("input[type='hidden'][name$='RequestVerificationToken']");
if (tokenField.length == 0) {
return null;
} else {
return {
name: tokenField[0].name,
value: tokenField[0].value
};
}
}
$.ajaxPrefilter(
function (options, localOptions, jqXHR) {
if (options.type !== "GET") {
let token = GetAntiForgeryToken();
if (token !== null) {
if (options.data.indexOf("X-Requested-With") === -1) {
options.data = "X-Requested-With=XMLHttpRequest" + ((options.data === "") ? "" : "&" + options.data);
}
options.data = options.data + "&" + token.name + '=' + token.value;
}
}
}
);})(jQuery);
控制器
public class AccountController : Controller
{
private CustomUserManager cman;
public AccountController()
{
cman = new CustomUserManager();
}
public AccountController(CustomUserManager customUserManager)
{
cman = customUserManager;
}
//[HttpGet]
[AllowAnonymous]
public PartialViewResult LoginView()
{
LoginViewModel lm = new LoginViewModel();
return PartialView("Login",lm);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(string mdata)
{
LoginViewModel model = LoginViewModel.FromFormData(mdata);
ValidationContext context = new ValidationContext(model);
List<ValidationResult> results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model, context, results);
if (isValid)
{
var user = await cman.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
//return JavaScript("location.reload(true);");
return Json(new { resultsuccess = "1" });
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
return PartialView(model);
}
}
else
{
results.ForEach(entry => ModelState.AddModelError("", entry.ErrorMessage));
return PartialView(model);
}
}
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Logout()
{
AuthenticationManager.SignOut();
return Redirect("~/");
}
protected override void Dispose(bool disposing)
{
if (disposing && cman != null)
{
cman.Dispose();
cman = null;
}
base.Dispose(disposing);
}
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
//AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await cman.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
}
OWIN 启动文件
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/")
//,CookieSecure = CookieSecureOption.SameAsRequest
});
// app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
所有这些东西在我的本地机器上运行良好。但是在开发服务器上,此代码仅在用户 login/password 错误时才有效:带有验证摘要的部分视图已被 return 编辑。如果 login/password 正确,则页面不会像我预期的那样刷新。如果我手动刷新页面,用户仍然没有登录。
当我尝试 AJAX 调用 Chrome 时失败并出现错误:ERR_CONNECTION_RESET 并且状态为 0,在 Edge 中错误是:网络错误 0x2eff,无法完成操作由于错误 00002eff。在 Firefox 中没有错误,但身份验证仍然无效。但是,它的状态为 200 OK,响应为:{"resultsuccess":"1"}(在 Firefox 中)。
我试过包装
await SignInAsync(user, model.RememberMe);
在 try/catch 的控制器中,但没有抛出异常。
也曾尝试 AJAX 从另一个控制器发出号召性用语 - 效果很好。
所以我什至不知道这个奇怪的问题可能在哪里:OWIN、XMLHTTPRequest 或 DEV 环境中的一些 IIS/machine 设置。
更新
已使用简单的非AJAX 登录表单创建了测试页面。同样的故事,用户在提交表单时在本地进行身份验证,而不是在 DEV 服务器上进行身份验证。所以看起来 XMLHTTPRequest 不是问题。
更新 2
因此,由于某种原因,它是在本地机器上设置的身份验证 cookie 而不是在 DEV 上设置的。
SignInAsync 方法调用失败或 IIS 未将 cookie 发送到浏览器或 ASP.Net
我的本地计算机和 DEV 服务器之间的主要区别是 IIS 版本:我本地的 IIS 10 (Windows 10) 与 DEV 上的 IIS 8.5 (Windows 服务器 2012)。已尝试使用 IIS 8.5 在虚拟机上部署站点 - OWIN 身份验证也不起作用。
尝试过解决方案
here,仍然无法正常工作。
因此,不幸的是,我不得不从项目中删除 OWIN 并实施自定义成员资格提供程序,以便在 DEV 服务器上进行身份验证。
我遇到了一些奇怪的问题。 我在弹出窗口中有一个登录表单。提交表单时 AJAX 向服务器发送请求,以加密的登录名和密码以及防伪令牌作为参数。控制器方法被调用,这个方法检查用户输入并且可以 return:
a) 如果用户输入不正确,则 HTML 对于部分视图(登录表单)有验证错误,注入而不是现有的登录表单
b) 如果用户输入正确,则刷新页面并为经过身份验证的用户重新显示。 我正在使用 OWIN 对用户进行身份验证。
Javascript - AJAX 调用
"use strict";(function ($, undefined) {
$(document).ready(function () {
$(document).on('submit', 'div.modal-dialog form', function () {
let $form = $(this),
//token = $('input[name="__RequestVerificationToken"]', form).val(),
logindata = $form.serialize(),
$formParent = $form.parent();
$.ajax({
url: $form.attr('action'),
type: 'POST',//form.attr('method'),
data: {
mdata: b64Encode(logindata),
},
success: function (result) {
if (result.resultsuccess) {
location.reload(true);
}
let $resultHtml = $('<div/>').html(result).contents();
let mbody = $resultHtml.find('div.modal-body');
if (mbody.length) {
$formParent.children().remove();
$formParent.html($resultHtml.find('div.modal-body').children());
}
},
error: function (result) {
console.log(result);
}
});
return false;
});
function GetAntiForgeryToken() {
let tokenField = $("input[type='hidden'][name$='RequestVerificationToken']");
if (tokenField.length == 0) {
return null;
} else {
return {
name: tokenField[0].name,
value: tokenField[0].value
};
}
}
$.ajaxPrefilter(
function (options, localOptions, jqXHR) {
if (options.type !== "GET") {
let token = GetAntiForgeryToken();
if (token !== null) {
if (options.data.indexOf("X-Requested-With") === -1) {
options.data = "X-Requested-With=XMLHttpRequest" + ((options.data === "") ? "" : "&" + options.data);
}
options.data = options.data + "&" + token.name + '=' + token.value;
}
}
}
);})(jQuery);
控制器
public class AccountController : Controller
{
private CustomUserManager cman;
public AccountController()
{
cman = new CustomUserManager();
}
public AccountController(CustomUserManager customUserManager)
{
cman = customUserManager;
}
//[HttpGet]
[AllowAnonymous]
public PartialViewResult LoginView()
{
LoginViewModel lm = new LoginViewModel();
return PartialView("Login",lm);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(string mdata)
{
LoginViewModel model = LoginViewModel.FromFormData(mdata);
ValidationContext context = new ValidationContext(model);
List<ValidationResult> results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model, context, results);
if (isValid)
{
var user = await cman.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
//return JavaScript("location.reload(true);");
return Json(new { resultsuccess = "1" });
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
return PartialView(model);
}
}
else
{
results.ForEach(entry => ModelState.AddModelError("", entry.ErrorMessage));
return PartialView(model);
}
}
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Logout()
{
AuthenticationManager.SignOut();
return Redirect("~/");
}
protected override void Dispose(bool disposing)
{
if (disposing && cman != null)
{
cman.Dispose();
cman = null;
}
base.Dispose(disposing);
}
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
//AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await cman.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
}
OWIN 启动文件
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/")
//,CookieSecure = CookieSecureOption.SameAsRequest
});
// app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
所有这些东西在我的本地机器上运行良好。但是在开发服务器上,此代码仅在用户 login/password 错误时才有效:带有验证摘要的部分视图已被 return 编辑。如果 login/password 正确,则页面不会像我预期的那样刷新。如果我手动刷新页面,用户仍然没有登录。
当我尝试 AJAX 调用 Chrome 时失败并出现错误:ERR_CONNECTION_RESET 并且状态为 0,在 Edge 中错误是:网络错误 0x2eff,无法完成操作由于错误 00002eff。在 Firefox 中没有错误,但身份验证仍然无效。但是,它的状态为 200 OK,响应为:{"resultsuccess":"1"}(在 Firefox 中)。
我试过包装
await SignInAsync(user, model.RememberMe);
在 try/catch 的控制器中,但没有抛出异常。
也曾尝试 AJAX 从另一个控制器发出号召性用语 - 效果很好。 所以我什至不知道这个奇怪的问题可能在哪里:OWIN、XMLHTTPRequest 或 DEV 环境中的一些 IIS/machine 设置。
更新
已使用简单的非AJAX 登录表单创建了测试页面。同样的故事,用户在提交表单时在本地进行身份验证,而不是在 DEV 服务器上进行身份验证。所以看起来 XMLHTTPRequest 不是问题。
更新 2 因此,由于某种原因,它是在本地机器上设置的身份验证 cookie 而不是在 DEV 上设置的。
SignInAsync 方法调用失败或 IIS 未将 cookie 发送到浏览器或 ASP.Net
我的本地计算机和 DEV 服务器之间的主要区别是 IIS 版本:我本地的 IIS 10 (Windows 10) 与 DEV 上的 IIS 8.5 (Windows 服务器 2012)。已尝试使用 IIS 8.5 在虚拟机上部署站点 - OWIN 身份验证也不起作用。 尝试过解决方案 here,仍然无法正常工作。
因此,不幸的是,我不得不从项目中删除 OWIN 并实施自定义成员资格提供程序,以便在 DEV 服务器上进行身份验证。