自定义成员资格提供程序 - 任务管理器






4.17/5 (5投票s)
本文是对上一篇文章内容的扩展。这是一个任务管理器应用程序,使用了上一部分设计的自定义提供程序。
引言
在上一篇文章中,我们学习了如何实现自定义成员资格提供程序,以有效地利用表单身份验证模型。现在,让我们将此应用程序扩展为一个任务管理器应用程序,该应用程序使用身份验证数据来区分用户并执行与特定用户相关的活动。请记住,您需要修改示例项目中的“AppDb”连接字符串以指向您的数据库。
文章第一部分可在此处找到。
扩展应用程序
此时,我们有一个基础应用程序。它使用我们的自定义成员资格提供程序类进行身份验证。现在,让我们将其扩展为一个用户可以输入他们想要记住的任务的应用程序。这是按用户进行的,因此我们需要一种方法来识别当前用户。添加任务和列出任务时都需要此信息。为此,首先我们需要创建一个表单身份验证票证,如下所示。这将在“Account”控制器中的“Register
”操作中。现在,向 AccountController.cs 文件中添加一个方法,如下所示。
private void SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
User user = new Models.User();
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
persistanceFlag,
user.GetUserIDByUsername(userName).ToString());
string encTicket = FormsAuthentication.Encrypt(authTicket);
this.Response.Cookies.Add(
new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
}
请注意传递给 FormsAuthenticationTicket
类构造函数的参数列表中的 user.GetUserIDByUserName(userName)
这一行。这是“userData
”属性,可用于存储我们的自定义数据,以便在其他地方使用。为了供您参考,此时,我已将当前用户的用户 ID 添加到表单身份验证票证中。下面我提供了 FormsAuthenticationTicket
类构造函数的签名。
public FormsAuthenticationTicket(int version, string name,
DateTime issueDate, DateTime expiration,
bool isPersistent, string userData);
下一步将是修改 LogOn
和 Register
方法,以便在用户成功登录或注册后调用此方法。下面我提供了 LogOn
和 Register
方法的修改版本。
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
SetupFormsAuthTicket(model.UserName,model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("",
"The user name or password provided is incorrect.");
}
}
return View(model);
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(
model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
FormsService.SignIn(model.UserName, false);
SetupFormsAuthTicket(model.UserName,false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("",
AccountValidation.ErrorCodeToString(createStatus));
}
}
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View(model);
}
What Next?
现在我们有了包含当前用户 ID 的表单身份验证票证。我们需要添加一种方式供视图使用。为此,让我们创建一个保存此票证和各页面所需信息的类。我们称之为“UserIdentity
”。它必须实现 IIdentity
接口。实现此接口的目的将在后面的步骤中变得清晰。请注意“UserId
”属性,它返回票证的“UserData
”,其中包含当前登录用户的用户 ID。
public class UserIdentity : IIdentity
{
private System.Web.Security.FormsAuthenticationTicket ticket;
public UserIdentity(System.Web.Security.FormsAuthenticationTicket _ticket)
{
ticket = _ticket;
}
public string AuthenticationType
{
get { return "User"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public string Name
{
get { return ticket.Name; }
}
public string UserId
{
get { return ticket.UserData; }
}
}
总结
现在,为了让所有页面都能使用此信息,我们需要将其设置到 HttpContext.Current.User
对象中。这在 Global.ascx 文件中完成,如下所示。向文件中添加一个 OnInit
方法,该方法附加一个 PostAuthenticateRequest
事件。
在 PostAuthenticateRequest
事件期间调用的方法中,请注意身份验证票证已被解密,并且 UserIdentity
对象的一个实例已被创建并传递给 GenericPrincipal
的构造函数,以创建一个 GenericPrincipal
对象。这就是为什么 UserIdentity
必须实现 IIdentity
接口的原因。最后,将 Principal 对象分配给 HttpContext.Current.User
对象。
public override void Init()
{
this.PostAuthenticateRequest +=
new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
string encTicket = authCookie.Value;
if (!String.IsNullOrEmpty(encTicket))
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(encTicket);
UserIdentity id = new UserIdentity(ticket);
GenericPrincipal prin = new GenericPrincipal(id, null);
HttpContext.Current.User = prin;
}
}
}
创建所需的表
现在我们在 User
对象中有了所需的信息。在查看如何使用它之前,让我们创建一个表来保存用户可以添加/查看的任务。表的结构非常简单。请注意,它与之前为保存用户而创建的 Users 表存在外键约束。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Tasks](
[TaskID] [int] IDENTITY(1,1) NOT NULL,
[TaskName] [varchar](50) NOT NULL,
[TaskDescription] [varchar](255) NOT NULL,
[TaskDateTime] [datetime] NOT NULL,
[UserID] [int] NOT NULL,
CONSTRAINT [PK_Tasks] PRIMARY KEY CLUSTERED
(
[TaskID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Tasks] WITH CHECK
ADD CONSTRAINT [FK_Tasks_Users] FOREIGN KEY([UserID])
REFERENCES [dbo].[Users] ([UserID])
GO
ALTER TABLE [dbo].[Tasks] CHECK CONSTRAINT [FK_Tasks_Users]
GO
“Tasks”存储库
现在我们有了表示用户任务的表,让我们创建一个对象来保存此表中的条目。下面是 TaskObj
。
[Table(Name = "Tasks")]
public class TaskObj
{
[Column(IsPrimaryKey = true,
IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int TaskID { get; set; }
[Column] public string TaskName { get; set; }
[Column] public string TaskDescription { get; set; }
[Column] public DateTime TaskDateTime { get; set; }
[Column] public int UserID { get; set; }
}
下一步将是创建将添加任务或按用户 ID 获取任务列表的存储库。附加的项目包含用于获取特定任务和删除任务的附加方法,这些方法将在本文的未来增强功能中使用。下面是任务存储库。
public class Task
{
private Table<TaskObj> taskList;
private DataContext context;
public Task()
{
string connectionString =
ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
context = new DataContext(connectionString);
taskList = context.GetTable<TaskObj>();
}
public IEnumerable<TaskObj> GetTasksByUserID(int userID)
{
return taskList.Where(t => t.UserID ==
userID).OrderByDescending(t => t.TaskDateTime);
}
public bool AddTask(TaskObj task)
{
taskList.InsertOnSubmit(task);
context.SubmitChanges();
return true;
}
}
在视图中使用可用的用户 ID
我们来到了最有趣的部分(至少对于本文而言!)。我们如何使用与表单身份验证票证一起存储的用户相关信息?您离看到它不远了!您只需从 System.Security.Principal.IPrincipal.Controller
对象中获取 User.Identity
对象。它的类型是我们在构造 UserIdentity
类型时看到的 IIdentity
。因此,我们可以安全地将其转换为 UserIdentity
。
如果您还记得 UserIdentity
类型,我们添加了一个名为 UserId
的属性,其中包含我们在表单身份验证票证中存储的用户数据。这可以在控制器中用于添加任务或列出与此用户 ID 相关的任务。请查看下面的添加任务的控制器 - 查看用户 ID 是如何获得的。另外,请注意控制器上的 Authorize
属性。一旦我们获得了用户 ID,我只需调用任务存储库来添加任务。
[Authorize]
[HttpPost]
public ActionResult AddTask(TaskObj task)
{
UserIdentity identity = (UserIdentity)User.Identity;
Task _task = new Task();
task.UserID = int.Parse(identity.UserId);
task.TaskDateTime = DateTime.Now;
_task.AddTask(task);
return View(task);
}
现在,让我们在母版页中添加一个名为“任务管理器”的选项卡,这样我们就可以有一个链接来访问任务管理器。打开 Views/Shared 文件夹下的 Site.Master 并添加以下行。
<li>
<%: Html.ActionLink("Task Manager", "Tasks", "Home") %>
</li>
现在,在 Home 文件夹下添加 Tasks.aspx 和 AddTask.aspx 视图。Tasks
视图的模型是强类型为 TaskObj
的 IEnumerable
,并使用 foreach
简单地列出任务。Tasks
的控制器使用 Tasks
存储库中的 GetTasksByUserID
方法获取用户的任务列表。它从 UserIdentity
对象中获取用户的 ID,如 AddTask
控制器中所示。
另一方面,AddTask
是强类型为 TaskObj
类型,我们使用 Html.BeginForm
来显示带表单的用户,并且在控制器中,我们调用 Tasks
存储库中的 AddTask
方法来添加任务。您可以查看附加项目中的视图。
未来改进计划
将来,我计划更新本文以使用依赖注入,我还计划为添加/编辑/删除任务添加 AJAX 支持,使用一个通用的管理页面,而不是为添加/编辑/删除使用单独的页面。
结论
在我第一篇文章中,我解释了如何使用自定义成员资格提供程序来控制用户如何在我们的应用程序中进行身份验证,在这篇文章中,我将该应用程序扩展为执行比仅身份验证用户更有用的事情。希望这篇文章对大家有所帮助!请随时在下面的评论部分发布评论/问题。再次感谢您阅读我的文章!
历史
第一个版本发布。