- A+
WebForms中的表单身份验证
在讲解MVC提供的安全策略之前,还是先看下WebForms中常见的表单身份验证(Forms Authentication),这种身份验证的过程也很简单:
- 用户提供登录信息(比如用户名和密码)。
- 登录信息验证通过后,会创建一个包含用户名的FormsAuthenticationTicket对象。
- 对此Ticket对象进行加密,并将加密结果以字符串的形式保存到浏览器Cookie中。
后会的所有HTTP请求,都会带上这个Cookie并由WebForms进行比对,同时对外公开如下两个属性:
- HttpContext.User.Identity.IsAuthenticated
- HttpContext.User.Identity.Name
在Web.config中,我们一般需要配置登录页面(loginUrl)、登录后的跳转页面(defaultUrl),
登录后的保持时间(timeout)等信息:
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/default.aspx" timeout="120"
defaultUrl="~/main.aspx" protection="All" path="/" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
</system.web>
上面这个配置拒绝了所有用户的匿名访问,当然我们在<system.web>节的外面更改指定目录的访问权限,比如:
<location path="res"> <system.web> <authorization> <allow users="*" /> </authorization> </system.web> </location>
这个配置允许匿名用户对res目录的访问(一般是静态资源)。
MVC中的表单身份验证
MVC对验证模型进行了重写,但是基本的原理没有变化,我们更关注的是不同点:
- WebForms中基于目录进行权限控制。
- MVC中对控制器或者控制器的方法进行权限控制。
理解这一点也不难,因为MVC中没有和物理目录对应的URL,并且同一个控制器方法可能会对应多个访问URL,这一过程是由路由引擎配置的,在第一篇文章中有简单介绍。
Authorize注解
在MVC中,我们要保护的资源不是文件夹目录,而是控制器和控制器方法,所以MVC提供了授权过滤器(Authorize Filter)对此进行保护,它是以数据注解的形式提供的。
[Authorize]
public class StudentsController : Controller
{
...
}
这里是对整个控制器进行了保护,防止匿名用户访问,这时访问会得到一个错误的页面:
配置表单身份验证
现在添加配置信息:
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" defaultUrl="~/Students" timeout="120" protection="All" path="/" />
</authentication>
</system.web>
指定了登录页面~/Home/Login,登录后的页面是~/Students,现在再来浏览页面:
http://localhost:55654/Students
这次访问有两个HTTP请求,并且浏览器地址栏的URL改变了:
http://localhost:55654/Home/Login?ReturnUrl=%2fStudents
这样的地方我们很熟悉,ReturnUrl参数指定了登录成功后需要调整的页面,而~/Home/Login则是我们刚刚在Web.config中配置的登录页面。
两个HTTP请求中的第一个,响应码是302,这是一个重定向响应,浏览器会自动识别302响应并跳转到响应头中Location指定的网址。所以第二个请求是由浏览器发起的,但是我们尚未定义Login页面,所以返回404未找到。
创建登录页面
定义Home/Login控制器方法:
public class HomeController : Controller
{
public ActionResult Login()
{
return View();
}
}
在操作方法内部点击右键,选择[添加视图…]菜单项:
在弹出的向导对话框中,选择[Empty(without Model)],我们来手工创建视图内容:
完成的视图页面:
@{
ViewBag.Title = "Login";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<input type="text" name="UserName" />
<input type="password" name="Password" />
<input type="submit" value="登录" />
}
点击[登录]按钮,表单会通过POST请求提交到Login方法:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(string UserName, string Password) { if(UserName == "sanshi" && Password == "pass") { FormsAuthentication.RedirectFromLoginPage("sanshi", false); } return View(); }
这里硬编码了管理员的用户名和密码,在实际应用中可能需要从数据库中读取。
在布局中显示登录状态
接下来,我们需要在布局页面(Shared/_Layout.cshtml)中放置登录后的信息以及[退出系统]按钮:
@if (User.Identity.IsAuthenticated)
{
using (Html.BeginForm("Logout", "Home", FormMethod.Post, new { id = "logoutForm" }))
{
@Html.AntiForgeryToken()
<ul class="nav navbar-nav navbar-right">
<li><a href="javascript:;">Hello, @User.Identity.Name</a></li>
<li><a href="javascript:;" id="logout">退出系统</a></li>
</ul>
}
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("登录", "Login", "Home")</li>
</ul>
}
这段代码有两层逻辑:
- 如果用户已经验证过身份,则显示一个表单,里面放置[Hello, sanshi]以及一个登录按钮。受限于Bootstrap的内置样式,这里只能通过a标签来取代input标签,在页面底部还会注册脚本来处理按钮点击事件。
- 如果是匿名用户,则显示[登录]的超链接。
实现[退出系统]功能
注册[退出系统]按钮的客户端处理脚本,由于在生成表单标签时(Html.BeginForm),我们设置了表单标签的id属性,所以点击[退出系统]按钮时简单提交表单即可:
<script>
$(function () {
$('#logout').click(function () {
$('#logoutForm').submit();
});
});
</script>
[退出系统]按钮的后台逻辑,需要先清空客户端Cookie,然后执行客户端跳转:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
运行效果
来看下页面运行效果,首先是登录页面:
登录成功后,直接跳转到~/Students页面:
这样就完成了
- 我的微信
- 这是我的微信扫一扫
- 我的微信公众号
- 我的微信公众号扫一扫