我做了什么
我正在构建一个 REST 风格的 WebAPI 应用程序。我正在尝试在这里实现表单身份验证,因此客户端可以允许用户从浏览器登录/注册/注销。我正在使用 simpleMembership,用户存储在我的应用程序的 simpleMembership 数据库表中。作为第一步,我在我的 AccountsController 中实现了一个登录方法:
[System.Web.Http.HttpPost]
public HttpResponseMessage LogIn(LoginModel model)
{
if (ModelState.IsValid)
{
if (User.Identity.IsAuthenticated)
{
return Request.CreateResponse(HttpStatusCode.Conflict, "already logged in.");
}
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
//TODO: look up by id, not by name
var userProfile = _service.GetUser(WebSecurity.GetUserId(model.UserName));
return Request.CreateResponse(HttpStatusCode.OK, userProfile);
}
else
{
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
// If we got this far, something failed
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
问题
这是正在发生的事情:
- 我在
if (User.Identity.IsAuthenticated)
. - 使用 POSTMan 或 Fiddler 之类的东西,我尝试使用一些伪造的凭据登录。
- 正如预期的那样,我打断点并为 IsAuthenticated 获得“假”。
- 我跳到下一个“如果”,尝试登录失败,正如预期的那样。
- 语句中的代码
else
被命中,但一旦返回,1)我的断点再次被命中,2)主体设置为我的 windows 身份。两者都是出乎意料的,我正试图弄清楚如何纠正它。
预期行为
- 该方法只返回 401 Unauthorized,因为我的应用程序数据库中不存在虚假用户。
配置
我认为这是一个配置问题 - 在身份验证失败时,由于某种原因,主体会自动设置为我的 Windows 帐户。与我的配置相关的一些细节:
<authentication mode="Forms">
<forms loginUrl="~/" timeout="2880" />
</authentication>
- 我没有在应用程序的任何地方启用 Windows 身份验证。我的 web.config 在上面 ^
- 使用 IISManager 我已禁用匿名身份验证
- 在我的 applicationhost.config 中,我禁用了 Windows 和匿名身份验证
问题
- 为什么断点被击中两次,而不是 LogIn 方法只返回 Unauthorized ,因为最后一个“else”被击中?
- 为什么我的 Windows 凭据被用于这个意外和不希望的“环回”?
- 我怎样才能防止这种情况发生?或者:
- 我需要使用 MessageHandler 来设置主体吗?如果是这样,这对 [Authenticate] 和 IsAuthenticated 有何影响?有什么例子吗?
更新 1
我尝试注册以下处理程序:
public class PrincipalHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
System.Threading.CancellationToken
cancellationToken)
{
HttpContext.Current.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
Thread.CurrentPrincipal = HttpContext.Current.User;
return await base.SendAsync(request, cancellationToken);
}
}
现在主体从未设置为我的 Windows 身份。从 POSTMan(浏览器扩展),if 语句被击中两次,我得到一个弹出窗口,要求提供凭据。正如预期的那样,从 Fiddler 那里,我得到了 401 Unauthorized。
新问题
- 如何根据响应 cookie 中的内容设置主体,并针对我的应用程序的简单成员表进行身份验证?
更新 2
这篇文章似乎让我朝着正确的方向前进——在我的 LoginMethod 中使用 Membership 而不是 User:
ASP.NET MVC - 设置自定义 IIdentity 或 IPrincipal
将继续更新进展,但我仍然愿意接受建议/建议。
更新 3 我从来没有像现在这样既放松又生气——这里不需要自定义处理程序。我的项目属性中启用了 Windows 身份验证。登录和注销的工作解决方案在这里,并且与我的会员表“一起工作”:
public HttpResponseMessage LogIn(LoginModel model)
{
if (ModelState.IsValid)
{
if (User.Identity.IsAuthenticated)
{
return Request.CreateResponse(HttpStatusCode.Conflict, "already logged in.");
}
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return Request.CreateResponse(HttpStatusCode.OK, "logged in successfully");
}
else
{
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
// If we got this far, something failed
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
public HttpResponseMessage LogOut()
{
if (User.Identity.IsAuthenticated)
{
WebSecurity.Logout();
return Request.CreateResponse(HttpStatusCode.OK, "logged out successfully.");
}
return Request.CreateResponse(HttpStatusCode.Conflict, "already done.");
}