惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Webroot Blog
Webroot Blog
U
Unit 42
A
About on SuperTechFans
宝玉的分享
宝玉的分享
月光博客
月光博客
C
CERT Recently Published Vulnerability Notes
P
Privacy International News Feed
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Securelist
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
B
Blog
I
Intezer
Last Week in AI
Last Week in AI
T
Threat Research - Cisco Blogs
V
V2EX
L
LangChain Blog
AI
AI
G
GRAHAM CLULEY
T
Tor Project blog
人人都是产品经理
人人都是产品经理
D
Docker
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
I
InfoQ
Y
Y Combinator Blog
C
Comments on: Blog
GbyAI
GbyAI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
H
Help Net Security
Vercel News
Vercel News
T
Tenable Blog
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿

博客园 - 旋风

ASP.NET MVC3 Razor视图引擎-基础语法 二 ASP.NET MVC3 Razor视图引擎-基础语法 asp.net中 %= 与%# 的区别 ASP.NET MVC3 Model验证总结 ViewData与ViewBag比较 (十九): MVC 3.0 实例之使用开源控件实现表格排序和分页 (十八): MVC 3.0 实例系列之表格中合并排序、分页和筛选 (十七): MVC 3.0 实例之表格中数据的筛选 (十六): MVC 3.0 实例系列之表格数据的分页 (十五): MVC 3.0 实例系列之表格的排序 (十四): MVC 3.0 实例系列之创建数据表格 (十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击 (十二): MVC 3.0 使用自定义的Html控件 (十一): MVC 3.0 使用筛选器 (九): MVC 3.0 验证你的Model (八): MVC 3.0 传递和保存你的Model 七): MVC 3.0 新的Razor引擎 (六): MVC 3.0创建你的View视图 (五): 入手Controller/Action
(十): MVC 3.0 使用 Forms身份验证
旋风 · 2012-12-07 · via 博客园 - 旋风

概述

许多 Web 应用程序都要求能够限制对某些资源(例如特定页面)的访问,以确保只有经过身份验证的用户才能访问这些资源。 ASP.NET MVC 的默认 Web 应用程序项目模板提供了一个控制器以及一些数据模型和视图,您可使用这些组件为应用程序添加 ASP.NET 窗体身份验证功能。 借助该内置功能,用户可以注册、登录和注销,以及更改自己的密码。 对于许多应用程序,此功能可提供足够的用户身份验证级别。

页面控件引用

在MVC 3.0的项目模板里面的Shared文件夹中,我们可以看到名为_LogOnPartial.cshtml的页面,打开页面代码

复制代码

@if(Request.IsAuthenticated) {
<text>欢迎使用 <b>@Context.User.Identity.Name</b>!
[ @Html.ActionLink("注销", "LogOff", "User") ]
</text>
}
else {
@:[ @Html.ActionLink("登录", "LogOn", "User") ]
}

复制代码

我们可以看到该页面只是根据获取系统中的用户登录名,<b>@Context.User.Identity.Name</b>判断是登录还是注销的。

然后我们看看在什么地方用到了该页面?

MVC 3.0的项目模板的Shared文件夹中,我们在_Layout.cshtml页面中找到了,这个页面就是项目的整个母版页,相当于.Master页面。

代码

<div id="logindisplay">
@Html.Partial("_LogOnPartial")
</div>

这样简单的引用就可以将其他页面作为一个控件引用到该页面来。

实现登录

在前面的判断用户登录名时,我们可以看到有如下代码

@:[ @Html.ActionLink("登录", "LogOn", "User") ]

该代码指定登录在UserController的LogOn方法。

创建用户Model类

复制代码

publicclass User
{
publicint ID { get; set; }

[DisplayName(

"姓名")]
[Required(ErrorMessage
="姓名不能为空")]
publicstring Name { get; set; }

[DisplayName(

"密码")]
[Required]
[DataType(DataType.Password)]
publicstring Password { get; set; }

[Display(Name

="记住我?")]
publicbool RememberMe { get; set; }public Roles Roles { get; set; }

}

复制代码

创建角色Model类,便于后面页面角色的管理

代码:

publicclass Roles
{
publicint ID { get; set; }
publicstring Name { get; set; }
}

当前一个用户对应一个角色,不是一对多。后面有空会做个例子出来。

所以我们创建UserController在UserController添加LogOn方法

代码

复制代码

// **************************************
// URL: /User/LogOn
// **************************************

public ActionResult LogOn()
{
return View();
}

复制代码

创建视图

代码

复制代码

@model MvcApplication.Models.User
@{
ViewBag.Title = "用户登录";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>
用户登录
</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>登录信息</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.RememberMe)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.RememberMe)
@Html.ValidationMessageFor(model => model.RememberMe)
</div>
<p>
<input type="submit" value="登录"/>
</p>
</fieldset>
}
<div>
@Html.ActionLink("返回主页", "Index")
</div>

复制代码

有了界面,但是我们还没有对数据的操作

业务逻辑接口

创建一个名为IUserBusiness接口

复制代码

publicinterface IUserBusiness
{
///<summary>
/// 获取用户角色
///</summary>
///<param name="userName"></param>
///<returns></returns>
Roles GetRoles(string userName);
///<summary>
/// 根据用户名和密码获取用户信息
///</summary>
///<param name="name"></param>
///<param name="password"></param>
///<returns></returns>
User GetByNameAndPassword(string name, string password);
///<summary>
/// 登录
///</summary>
///<param name="userName"></param>
///<param name="createPersistentCookie"></param>
void SignIn(string userName, bool createPersistentCookie);
///<summary>
/// 注销
///</summary>
void SignOut();
}

复制代码

实现接口

复制代码

publicclass UserBusiness : IUserBusiness
{
///<summary>
/// 作为模拟的数据集
///</summary>
privatestatic User[] UserList =new[]
{
new User{ ID =1, Name ="张三", Password ="111111", Roles =new Roles{ID=101,Name ="employee"}},
new User{ ID =2, Name ="李四", Password ="111111", Roles =new Roles{ID =102,Name ="manager"}},
new User{ ID =3, Name ="admin", Password ="admin", Roles =new Roles{ID =103,Name ="admin"}}
};
///<summary>
/// 根据用户名获取角色
///</summary>
///<param name="userName"></param>
///<returns></returns>
public Roles GetRoles(string userName)
{
return UserList
.Where(o
=> o.Name == userName)
.Select(o
=> o.Roles)
.FirstOrDefault();
}
///<summary>
/// 根据用户名和密码获取用户
///</summary>
///<param name="name"></param>
///<param name="password"></param>
///<returns></returns>
public User GetByNameAndPassword(string name, string password)
{
return UserList
.FirstOrDefault(u
=> u.Name == name && u.Password == password);
}
///<summary>
/// 登录
///</summary>
///<param name="userName"></param>
///<param name="createPersistentCookie"></param>
publicvoid SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) thrownew ArgumentException("值不能为 null 或为空。", "userName");

FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}

///<summary>
/// 注销
///</summary>
publicvoid SignOut()
{
FormsAuthentication.SignOut();
}
}

复制代码

上面的角色获取和用户名密码获取都可以变化为数据库里面的数据。而登录只是把获取的数据添加到了cookies里面。注销亦然是使用了该机制。

所以上面登录中的

FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);

完全可以换成

复制代码

                    FormsAuthenticationTicket ticket =new FormsAuthenticationTicket(
1,
user.Name,
DateTime.Now,
DateTime.Now.Add(FormsAuthentication.Timeout),
user.RememberMe,
user.Name
);
HttpCookie cookie
=new HttpCookie(
FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(ticket));
Response.Cookies.Add(cookie);

复制代码

同样实现了登录的操作。

方法调用

我们在实现操作的时候要调用IUserBusiness接口中的相应方法来实现。

所以参考微软的做法,重写了Initialize方法,把IUserBusiness接口作为一个属性在程序中使用。

代码:

复制代码

public IUserBusiness UserBusiness { get; set; }protectedoverridevoid Initialize(RequestContext requestContext)
{
if (UserBusiness ==null) { UserBusiness =new UserBusiness(); }base.Initialize(requestContext);
}

复制代码

这样,每次加载的时候都会根据相应的方法去实例IUserBusiness。

在UserController中新建Post的LogOn方法

代码

复制代码

        [HttpPost]
public ActionResult LogOn(User user, string returnUrl)
{
if (ModelState.IsValid)
{
if (UserBusiness.GetByNameAndPassword(user.Name, user.Password)!=null)
{
UserBusiness.SignIn(user.Name, user.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError(
"", "提供的用户名或密码不正确。");
}
}
// 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(user);
}

复制代码

效果

点击登录

登陆后

注销用户

在UserController中添加LogOff的注销方法

复制代码

// **************************************
// URL: /User/LogOff
// **************************************

public ActionResult LogOff()
{
UserBusiness.SignOut();
return RedirectToAction("Index", "Home");
}

复制代码

同样调用了 业务逻辑里面的注销方法。

权限判断

在之前我们加入了Roles也在业务逻辑里面添加了获取Role的方法,那么我们在什么时候来取得该用户的Role呢?

我觉得是在页面验证之后要授权时,该方法在全局的Global.asax中,我们需要委托该事件。

委托时我们再去根据当前用户获取角色,或者此时早已经将角色获取好了,只是加入到系统的Context之用。

代码

复制代码

public MvcApplication()
{
AuthorizeRequest
+=new EventHandler(MvcApplication_AuthorizeRequest);
}
void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
{
IIdentity id
= Context.User.Identity;
if (id.IsAuthenticated)
{
var roles
=new UserBusiness().GetRoles(id.Name);
string[] rolelist =newstring[] { roles.Name };
Context.User
=new GenericPrincipal(id, rolelist);
}
}

复制代码

然后我们在UserController只用需要权限的方法上加上对应的[Authorize]标志即可
如代码

复制代码

//新建
// GET: /User/Create

[Authorize]
public ActionResult Create()
{
return View();
}

复制代码

如果你觉得,这样只是对于登录后所有的人有效,而你需要对指定的角色有效,你可以尝试用下面的方法。

代码如下

复制代码

//新建
// GET: /User/Create

[Authorize(Roles
="admin")]
public ActionResult Create()
{
return View();
}

复制代码

此时如果你的用户不是admin那么你就请求不了该Create方法

点击之后

同样你可以重写该错误方法,让他弹出错误页面或者什么,那就看个人喜好了。

总结

其实像这种方法不仅仅是MVC特有的,在很多框架或者程序里面都用了类似的方法,总之还是借用了微软本身的这种窗体身份验证的特性。上面的方法只是一种思想,不一定会在程序中用到,现在很多老鸟都已经摆脱这种老套的身份验证了,对于新手菜鸟们拿来学习一下我觉得还是无可厚非的。