ASP.NET MVC 4 表单身份验证自定义






4.85/5 (21投票s)
根据您的需求自定义 MVC 4 中的基于表单的身份验证。
引言
我正在 ASP.Net MVC 4 项目中进行一个 POC(概念验证),其中我必须自定义用户身份验证要求,以便它可以验证活动目录用户,而该用户也必须存在于我的项目数据库中。我已经搜索了很多,并阅读了许多文章,但没有找到任何直接的解决方案。所以我想把它拿出来和大家分享。
最初我以为我会使用 Windows 身份验证,并在 *global.asax* 的 Application_AuthenticateRequest
事件中编写自定义代码来验证我的项目数据库中的用户。但主要问题是,如果数据库中的用户验证失败,很难(我找不到方法)重置身份验证 cookie 来将用户标记为未经身份验证的用户。简而言之,用户始终通过 Windows 身份验证进行身份验证。您可以看到 Request.IsAuthenticated
标志始终为 TRUE
。
所以我决定使用表单身份验证,在其中我将绕过登录页面,并从 Request.ServerVariables["LOGON_USER"]
读取 Windows 登录用户,然后将在数据库中验证用户。
背景
这篇文章对于那些正在寻找 MVC 4 应用程序的单点登录(SSO)功能,同时又希望用户在您的项目数据库的用户表中得到验证的人来说非常有用。请注意,本文将基于 MVC 4,我使用 VS2010 SP1 来创建示例代码。
理解 MVC 中的表单身份验证
您可以找到大量关于表单身份验证的文章。所以我不会写重复的无聊内容,而是会揭开当您在创建 MVC 项目时选择“Internet Application”(Internet 应用程序)模板时在项目中看到的抽象。让我们一步一步来。
- 文件 -> 新建 -> 项目 -> ASP.NET MVC 4 应用程序 点击“确定”按钮
- 项目模板 -> Internet Application
- 视图引擎 -> Razor,然后单击“确定”按钮创建项目
现在项目已创建。如果您查看 web.config 文件,您会找到以下身份验证标签,该标签声明项目将使用“Forms”(表单)身份验证。
<authentication mode="Forms" /><forms loginUrl="~/Account/Login" timeout="2880" /></authentication>
现在,如果您尝试编译项目,您将能够成功编译并运行该项目。第一个问题发生在您单击“Login”(登录)按钮时。您可以在 "InitialiseSimpleMembershipAttribute.cs" 文件中看到以下错误。
上面的错误发生是因为代码无法初始化/创建 SQL Express 中的 membership 表。您可以在 web.config 文件中找到以下 *connectionstring*。
<add name="DefaultConnection" connectionstring="Data Source=.\SQLEXPRESS;
Initial Catalog=aspnet-MvcApplication1-20130603132719;Integrated Security=SSPI"
providername="System.Data.SqlClient" />
我建议您更改默认连接字符串,使其指向您的 SQL 服务器项目数据库,如下所示:
<add name="DefaultConnection"
connectionString=quot;server=SQl Server machine name Integrated Security=false;
User ID=sa; Password=password; database= your project database;
providerName="System.Data.SqlClient" /></p>
现在您再次运行应用程序并单击“Login”(登录)按钮,您将能够看到如下所示的登录页面:
好了,现在如果您检查数据库,您将能够看到 5 个新表已由 membership provider 自动创建。它们如下:
- dbo.UserProfile(此表包含您尝试从应用程序注册用户时用户的姓名)
- dbo.webpages_ Membership(此表维护您尝试从应用程序注册用户时创建的用户的密码)
- dbo.webpages_OAuthMembership(如果您使用 OAuth,此表将包含用户 ID。超出本文范围)
- dbo.webpages_Roles(您可以在此表中创建角色,这些角色可在应用程序中授权期间用于
[Authorize]
属性) - dbo.webpages_UsersInRoles(此表包含用户及其角色关系)
现在让我们谈谈单击“Log in”(登录)按钮时会发生什么。首先。这是写在 *Account* Controller 中的代码,当您单击“Log in”(登录)按钮时会触发。
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName,
model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
相当简单的代码。魔法发生在 WebSecurity.Login()
方法。此方法首先检查提供的用户是否存在于 dbo.userprofile 表中。如果存在,那么此方法会检查提供的密码是否与 dbo.webpages_ Membership 表中的匹配。如果密码也匹配,那么此方法会读取与该用户关联的角色并将其设置在请求上下文中,并返回 true 表示登录成功。它还会内部将 Request.IsAuthenticated
标志设置为 true。这就是 MVC 4 项目中表单身份验证的全部内容。
身份验证自定义问题的解决方案
现在,既然您已经理解了表单身份验证的逻辑,让我们来谈谈使用表单身份验证解决单点登录问题的方案,其中用户还将在项目数据库中得到验证。
步骤 -1
在 *Web.Confile* 文件中,将 <Forms> 标签更改为以下标签:
<forms loginUrl="errors/InvalidUser" timeout="2880" />
步骤 -2
添加 Errors Controller 和以下 Action 方法:
public class ErrorsController : Controller
{
//
// GET: /Errors/
public ActionResult InvalidUser()
{
return PartialView("_InvalidUser");
}
public ActionResult UnAuthorizedUser()
{
return PartialView("_UNAuthorizedUser");
}
}
步骤 -3
添加一个共享视图 "_InvalidUser.cshtml" 并包含以下 Razor 代码:
@{
ViewBag.Title = "Invalid User";
}
<hgroup class="title">
<h1 class="error">Invalid User.</h1>
<h2 class="error">Please contact your Administrator.</h2>
</hgroup;>
添加另一个共享视图 "_UNAuthorizedUser.cshtml" 并包含以下 Razor 代码:
@{
ViewBag.Title = "Invalid User";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<hgroup class="title">
<h1 class="error">You are not authorized to view this page.</h1>
<h2 class="error">Please contact your Administrator.</h2>
</hgroup>
步骤 -4
将现有的 _LoginPartial.cshtml 部分视图更改为以下 Razor 代码:
@if (Request.IsAuthenticated)
{
<text>
Hello, @User.Identity.Name
</text>
}
步骤 -5
让我们在应用程序数据库中创建我们自己的用户表。
CREATE TABLE [dbo].[AppUser]( [USerID] [int] IDENTITY(1,1) NOT NULL,
[LoginName1] [nvarchar]
(50) NULL, )
步骤 -6
手动在用户表中创建用户数据。
Insert Into dbo.AppUser values(1,'<your windows user name>');
步骤 7
在 Membership 表中创建数据。
insert into dbo.webpages_Membership(UserId,PasswordFailuresSinceLastSuccess,Password)
values
(1,0,'AJfhqOHKFeLY8aHVGCAwf0dnN6QkGPv09Hj5sQaG2FQsdIk9p7zniTJmb6tMQK/HIQ==')
请注意:上面查询中显示的密码的加密值是“1”,因为 WebSecurity.Login
无法接收空密码,所以我将此密码硬编码为“1”,并且我计划为每个用户创建相同的加密密码。由于我们采用单点登录,密码在这里意义不大。
步骤 -8
根据您的需求在角色表中创建角色。
步骤 -9
转到 InitializeSimpleMembershipAttribute.cs 文件并更改以下代码,其中“User”是我们新创建的表。
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"User", "UserId", "UserName", autoCreateTables: true);
第 10 步
将以下属性添加到 Home Controller。
[Authorize]
[InitializeSimpleMembership]
步骤 -11
将 Home Controller 的 Index Action 更改为以下代码:
[AllowAnonymous]
public ActionResult Index()
{
string username;
var logonUser = Request.ServerVariables["LOGON_USER"];
username = logonUser.Split('\\')[1].ToString();
if (!Request.IsAuthenticated)
{
username = "sanjiv";
if (WebSecurity.Login(username, "2", persistCookie: false))
{
return RedirectToAction("Index", "Home");
}
else
{
return RedirectToAction("InvalidUser", "Errors");
}
}
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
步骤 - 12
从项目中完全删除 account controller (AccountController.cs)。
步骤 -13
现在您运行应用程序,将能够看到以下带有已登录用户信息 的屏幕:
摘要
我将回顾本文中执行的活动。
- 我们已经从项目中完全删除了 Account Controller,并通过 "Home" controller 的 "Index" action 管理了身份验证。这就是为什么我们必须为 "Index" action 提供
[AllowAnonymous]
属性的原因。在此身份验证过程中,我们从 Server variables 读取已登录的用户名,而不让用户输入用户名,因此它表现得像一个单点登录应用程序。 - 其次,我们已将 "
InitializedSimpleMembership
" provider 更改为连接到我们的 Local DB。我们还在项目数据库中的自己的 AppUSer 表中管理了用户数据。 - 第三,也是最重要的一点,我们更改了
_LoginPartial
部分视图,并创建了自己的错误视图来显示“无效用户。”错误消息。