System.InvalidOperationException 的未处理异常破坏了我的 MVC 应用程序?

An unhandled exception of System.InvalidOperationException breaks my MVC App?

我采用了 building a OWIN login from a empty MVC 的概念,并且在创建身份声明以放入 URL 之后,我才开始添加使用我的数据库登录用户的部分。

这是我创建用户登录声明的代码

public class AuthenticationController : Controller
{
    IAuthenticationManager Authentication
    {
        get { return HttpContext.GetOwinContext().Authentication; }
    }

    [GET("Login")]
    public ActionResult Show()
    {
        return View();
    }

    [POST("Login")]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel input)
    {
        if (ModelState.IsValid)
        {
            if (input.HasValidUsernameAndPassword())
            {
                var identity = new ClaimsIdentity(new[] 
                {
                        new Claim(ClaimTypes.Name, input.Username),
                },
                DefaultAuthenticationTypes.ApplicationCookie,
                ClaimTypes.Name, ClaimTypes.Role);

                // if you want roles, just add as many as you want here (for loop maybe?)
                if (input.isAdministrator)
                {
                    identity.AddClaim(new Claim(ClaimTypes.Role, "Admins"));
                }
                else
                {
                    identity.AddClaim(new Claim(ClaimTypes.Role, "User"));
                }
                // tell OWIN the identity provider, optional
                // identity.AddClaim(new Claim(IdentityProvider, "Simplest Auth"));

                Authentication.SignIn(new AuthenticationProperties
                {
                    IsPersistent = input.RememberMe
                }, identity);

                return RedirectToAction("show", "authentication");
            }
        }
        return View("show", input);
    }

    [GET("logout")]
    public ActionResult Logout()
    {
        Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        return RedirectToAction("Login");
    }
}

这是我的 Show.cshtml

代码
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Details</h4>
    <hr />
    @Html.ValidationSummary(true)

    <div class="form-group">
        @Html.LabelFor(model => model.Username, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Username)
            @Html.ValidationMessageFor(model => model.Username)
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Password, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Password)
            @Html.ValidationMessageFor(model => model.Password)
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.RememberMe, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.RememberMe)
            @Html.ValidationMessageFor(model => model.RememberMe)
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Login" class="btn btn-default" />
        </div>
    </div>
</div>
}

这是我调用数据库并为登录用户存储数据的代码

public class LoginModel
{
    [Required]
    public string Username { get; set; }
    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    public bool RememberMe { get; set; }

    public bool isAdministrator { get; set; }

    public bool HasValidUsernameAndPassword()
    {
        bool result = false;
        int countChecker = 0;
        using (SqlConnection connection = new SqlConnection("server=server; database=db; user id=user; password=user"))
        {
            connection.Open();
            using (SqlCommand command = new SqlCommand("SELECT count(*) FROM ACCOUNTS WHERE active = 1 AND UserName = @param1 AND PasswordField = @param2", connection))
            {
                command.Parameters.Clear();
                command.Parameters.AddWithValue("@param1", Username);
                command.Parameters.AddWithValue("@param2", Password);
                SqlDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        countChecker = Convert.ToInt32(reader[i].ToString());

                    }
                }
                if (countChecker > 0)
                {
                    result = true;
                    using (SqlConnection connect = new SqlConnection("server=server; database=db; user id=user; password=user"))
                    {
                        connect.Open();
                        using (SqlCommand com = new SqlCommand("SELECT Administrator FROM ACCOUNTS WHERE active = 1 AND UserName = @param1 AND PasswordField = @param2", connect))
                        {
                            com.Parameters.Clear();
                            com.Parameters.AddWithValue("@param1", Username);
                            com.Parameters.AddWithValue("@param2", Password);
                            SqlDataReader read = com.ExecuteReader();
                            while (read.Read())
                            {
                                for (int i = 0; i < read.FieldCount; i++)
                                {
                                    isAdministrator = Convert.ToBoolean(read[i].ToString());
                                }
                            }
                        }
                        connect.Dispose();
                        connect.Close();
                    }
                }
                else
                {
                    result = false;
                }
            }
            connection.Dispose();
            connection.Close();
        }
        return result;
    }
}

我的登录(LoginModel 输入)完成,returns 到 Show.cshtml,然后在 @Html.AntiForgeryToken() 处出现异常。我得到的例外是:

A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.

我是使用 MVC 的新手,我无法判断 ActionResult Login(LoginModel 输入)中缺少两者中的哪一个。有人可以向我解释我错过了哪一个吗?

我将 Corey 的建议添加到我的 Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
        AttributeRoutingConfig.RegisterRoutes(RouteTable.Routes);
    }
}

现在我明白了:

Additional information: A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' was not present on the provided ClaimsIdentity.

我改了 AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;至 AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;我再也没有例外。这是正确的语法吗?

这个article可能有帮助:

Unfortunately this error is a little confusing because it says “nameidentifier or identityprovider” even though you may have one of the two. By default you need both.

Anyway, if you’re not using ACS as your STS then the above error pretty much tells you what’s needed to solve the problem. You need to tell MVC which claim you want to use to uniquely identify the user. You do this by setting the AntiForgeryConfig.UniqueClaimTypeIdentifier property (typically in App_Start in global.asax). For example (assuming you want to use nameidentifier as the unique claim):

protected void Application_Start()
{
    ...

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}