使用mvc自定义登录搞乱登录部分视图

时间:2014-05-16 21:34:20

标签: asp.net-mvc asp.net-mvc-4 login

所以我在我的MVC应用程序中设置了一个自定义登录,似乎可以工作......

public ActionResult Login()
{
    return View();
}
[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (model.Login())
        {
            FormsAuthentication.SetAuthCookie(model.Username, true);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "Invalid email address or password.");
        }
    }

    // If execution got this far, something failed, redisplay form
    return View(model);
}

public class User
{
    public bool Login()
    {
        var user = db.Users.FirstOrDefault(u => u.EmailAddress == EmailAddress);

        if (user == null)
        {
            throw new ValidationException("User not found.");
        }
        else
        {
            // validates whether or not the password on the user record
            // that was retrieved by the query matches the password entered at login
            return Hashing.ValidatePassword(Password, user.Password);
        }
    }
}

不幸的是,它与默认的_LoginPartial.cshtml视图(如下所示)之间存在一些冲突:

@model LoganMVC.Models.User
@if (Request.IsAuthenticated) {
    <text>
        Hello, @Html.ActionLink(User.Identity.Name, "Manage", "Account", new { User.Identity.Name })!
        @using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" })) {
            @Html.AntiForgeryToken()
            <a href="javascript:document.getElementById('logoutForm').submit()">Log off</a>
        }
    </text>
} else {
    <ul>
        <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
        <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
    </ul>
}

ArgumentNullException突出显示此行......

  

您好,@ Html.ActionLink(User.Identity.Name,&#34; Manage&#34;,&#34; Account&#34;,new {User.Identity.Name})!

说...

  

值不能为null或为空。

很明显,不能为空或空的值为User.Identity.Name,但不清楚的是,为什么IsAuthenticated为真。这是表单身份验证,并且,只要软件知道(因为我杀死了调试服务器,关闭了解决方案并重新开始调试),应用程序从未运行过。

2 个答案:

答案 0 :(得分:1)

好吧,我一直在抨击各种坚硬的表面,试图弄清楚这一点,并最终做到了。

在查看我最初得到的错误之后:

  

值不能为null或为空。

然后是

  

提供的类型为System.Web.Security.FormsIdentity&#39;标记为IsAuthenticated = true但没有Name的值。默认情况下,防伪系统要求所有经过身份验证的身份都具有唯一的名称。如果无法为此标识提供唯一的名称,请考虑将静态属性AntiForgeryConfig.AdditionalDataProvider设置为可为当前用户提供某种形式的唯一标识符的类型的实例。

修复相当简单。

首先,我最初将登录cookie设置为持续存在。这意味着当浏览器关闭时它不会被删除,也不会过期。虽然这不会导致Value cannot be null or empty错误,但这是因为网站找到它并假设用户已经过身份验证。

<强> FIX
为了解决这个问题,我只是从我的浏览器中清除了cookie,将cookie保持为假。这让我更专注于实际问题。

值不能为空或空

这个问题实际上没有我想象的那么复杂。当我开始仔细研究事物时,我注意到以下代码中存在错误:

[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (model.Login())
        {
            FormsAuthentication.SetAuthCookie(model.Username, true);
            if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "Invalid email address or password.");
        }
    }

    // If execution got this far, something failed, redisplay form
    return View(model);
}

此帖子从表单(登录表单)中捕获我的用户模型的实例,因此仅包含EmailAddress和密码值。它还使用此模型中的Username值(为null)来设置Authentication Cookie。

我将.Login()方法更改为一个函数,如果找到了电子邮件地址,则该函数返回User,并且存储的哈希值与为登录时输入的密码的哈希值匹配。

因此,代码更改为以下内容(这只是更改的关键点)

// Account.Login Post
if (ModelState.IsValid)
{
    var user = model.Login(model.EmailAddress);
    if (user == null)
    {
        ModelState.AddModelError("", "Invalid email address or password.");
    }

    FormsAuthentication.SetAuthCookie(user.Username, true);
    // create session variables n such
}

// User.Login
public User Login(string EmailAddress
{
    var user = dbContext.Users.FirstOrDefault(u => u.EmailAddress == Email);

    if (user == null)
        throw new Exception("User not found.");

    if (Hashing.ValidatePassword(Password, user.Password))
        return user;

    return null; // if we got here, something went wrong
}

一旦我解决了这个问题,我后来又遇到了另一个问题。与Anti-Forgery系统有关的一些问题。

虽然不是解决方案,但我设法了解了一个解决方法并从我的_LoginPartial View中注释掉了以下一行:

@Html.AntiForgeryToken()

我担心安全性,但我认为在一个基本的asp.net网站上使用密码和盐渍密码的Forms Auth已经足够好多年了,所以我在这里不应该有任何问题

答案 1 :(得分:0)

你可能会错过一些东西。在您的控制器Account中,您有一个操作:

  public ActionResult Manage(string name) // I presume name
 {
  ....
 }

然后你的行应该是:

  Hello, @Html.ActionLink(User.Identity.Name, "Manage", "Account", new { name=User.Identity.Name })!

是routeValues的匿名对象 它是包含路径参数的对象。通过检查对象的属性,通过反射检索参数。该对象通常使用对象初始化器语法创建。(定义取自MSDN)它应格式良好。

我希望它能解决问题,否则你必须检查User.Identity是否与null不同。 我也注意到了这一行中的其他内容:

 var user = db.Users.FirstOrDefault(u => u.EmailAddress == EmailAddress);

EmailAddress之后的==来自哪里? 您应该为Login方法提供一个参数来替换您的第二个EmailAddress

   public bool Login(string emailAddress)
   {
    var user = db.Users.FirstOrDefault(u => u.EmailAddress == emailAddress);

    if (user == null)
    {
        throw new ValidationException("User not found.");
    }
    else
    {
        // validates whether or not the password on the user record
        // that was retrieved by the query matches the password entered at login
        return Hashing.ValidatePassword(Password, user.Password);
    }