在前面的章节中我们知道可以在MVC应用程序中使用[Authorize]特性来限制用户对某些网址(控制器/控制器方法)的访问,但这都是在对用户认证之后,而用户的认证则依然是使用ASP.NET平台的认证机制。

ASP.NET提供Windows和Forms两种身份验证,前者主要用于Intranet上域环境内,后者则更多的应用于Internet,这里我们只讨论后者。先从最简单的例子开始,我们在web.config中配置Forms认证方式:

...
<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880">
    <credentials passwordFormat="Clear">
      <user name="admin" password="secret" />
    </credentials>
  </forms>
</authentication>
...

这里设置认证方式为Forms,用户登录的地址为~/Account/Login,我们用最简单的方式创建用户信息,在credentials节中直接设置用户名称/密码。在创建页面之前我们先创建收集用户名和密码Model类:

using System.ComponentModel.DataAnnotations;

namespace SportsStore.WebUI.Models {

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

创建一个视图来收集用户名和信息:

@model SportsStore.WebUI.Models.LoginViewModel

@{
ViewBag.Title = "Admin: Log In";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>Log In</h1> <p>Please log in to access the administrative area:</p>
@using(Html.BeginForm()) {
@Html.ValidationSummary(true)
@Html.EditorForModel()
<p><input type="submit" value="Log in" /></p>
}

最后还需要在Account控制器的Login action中处理用户提交的用户名和密码完成用户认证:

[HttpPost]
public ActionResult Login(LoginViewModel model)
{ if (ModelState.IsValid)
{
bool result = FormsAuthentication.Authenticate(model.UserName, model.Password);
if (result)
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return Redirect(Url.Action("Index", "Admin"));
}
else
{
ModelState.AddModelError("", "Incorrect username or password");
return View();
}
}
else
{
return View();
}
}

调用FormsAuthentication.Authenticate()对用户名和密码验证,如何验证成功,调用FormsAuthentication.SetAuthCookie()设置用户验证的cookie并在响应中返回,在cookie过期之前用户再次访问时不再要求登录。

以上就是最简单的Forms身份验证过程,但实际的Internet应用用户的信息一般存储在数据库中,通过Membership provider利用数据库中的信息对用户验证,MVC4中微软为我们提供SQL membership provider、Universal membership provider和Simple membership provider,下面来看看如何具体如何使用它们。

SQL membership provider

在.NET 2.0中SQL membership provider就已经存在了,在visual studio 2012中使用empty模板创建一个MVC4的工程,web.config你不会看到任何membership provider相关的信息,默认使用的是Windows认证。在VS的Project菜单下打开Asp.net configurtion工具(在打开配置工具前记得编译下工程,否则会提示“选定的数据存储区出现问题”),在“安全”标签页面点击“选择身份验证类型”,配置工具询问“用户如何访问您的站点?”,选择“通过Internet”,点击“完成”后配置工具将自动在web.config中添加“<authentication mode="Forms" />”。配置工具仍然没有在web.config添加任何membership provider的信息,但是我们转到配置工具的“提供程序页面”,可以看到看到默认选中的是AspNetSqlMembershipProvider,同时配置工具会在工程的app_data目录下创建一个名为ASPNETDB.MDF的数据库,这是一个sql express的数据库,visual studio 2012中不能直接打开(VS用的是localdb),可以在SQL管理工具中附加到SQL EXPRESS的服务实例来查看。打开数据库可以看到数据库中添加了很多“aspnet_”为前缀的表和存储过程,这些都是SqlMembershipProvider需要的。

如果我们要使用自建的数据库来保存用户信息改如何操作呢?我们在Solution exploer中点击App_Start目录,右键菜单中选择添加->添加项目->SQL数据库创建一个localdb的数据库,添加相应的Connection字符串到web.config:

<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(localdb)\v11.0;AttachDbFileName=|DataDirectory|\mvc4empty.mdf;Initial Catalog=mvc4empty;Integrated Security=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>

我们还需要在web.config手工添加SqlMembershipProvider,让它使用上面的数据库连接:

<membership defaultProvider="mySqlMembershipProvider">
<providers>
<clear />
<add connectionStringName="DefaultConnection" enablePasswordRetrieval="false"
enablePasswordReset="true" requiresQuestionAndAnswer="false"
applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed"
passwordStrengthRegularExpression="" name="mySqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider" />
</providers>
</membership>

再次打开asp.net配置工具转到安全界面会提示错误“Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'”,配置工具试图调用相关的存储过程,但是数据库是我们手工创建的,不包含这些过程和数据表。我们可以使用aspnet_regsql.exe工具在我们的数据库中创建相关表和数据,C:\Windows\Microsoft.NET\Framework64\v4.0.30319和C:\Windows\Microsoft.NET\Framework64\v2.0.50727都有这个工具,没有细究两个版本的不同,这里使用.NET 4.0的版本。在aspnet_regsql工具选择服务器为“(localdb)\v11.0”,数据库列表中如果找不到新建的数据库,可以事先在sql manage studio中连接到服务引擎“(localdb)\v11.0”后附加该数据库(aspnet_reqsql也支持使用连接字符串作为参数,参见http://msdn.microsoft.com/en-us/library/ms229862(v=vs.100).aspx)。完成上述操作后,asp.net配置工具就可以在我们的数据库中创建管理用户了。

准备好Forms认证的配置,我们继续完善上面的例子,从控制器开始:

using System;
using System.Web.Mvc;
using System.Web.Security;
using SportsStore.WebUI.Models; namespace SportsStore.WebUI.Controllers
{ public class AccountController : Controller
{
public ViewResult Login(string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
return View();
} [HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid) return View();
var result = Membership.ValidateUser(model.UserName, model.Password);
if (result)
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return Redirect(returnUrl ?? Url.Action("Index", "Home"));
}
ModelState.AddModelError("", "Incorrect username or password");
return View();
} public ActionResult Logout(string returnUrl)
{
FormsAuthentication.SignOut();
return Redirect(returnUrl ?? Url.Action("Index", "Home"));
} public ViewResult Register()
{
return View();
} [HttpPost]
public ViewResult Register(LoginViewModel model)
{
if (!ModelState.IsValid) return View(model);
try
{
Membership.CreateUser(model.UserName, model.Password);
ViewBag.Registered = true;
}
catch (Exception exception)
{
ModelState.AddModelError("",exception.Message);
}
return View(model);
}
}
}

在用户登录时不再使用FormsAuthentication.Authenticate()认证用户,它仅读取web.config中credentials节的内容,我们需要改用Membership.ValidateUser()对用户密码校验。调用FormsAuthentication.SignOut()登出用户,它清除认证相关的cookie。Register() action用于创建用户,它调用Membership.CreateUser()创建一个用户保存到数据库中,对应的Register视图:

@model SportsStore.WebUI.Models.LoginViewModel

@{
ViewBag.Title = "User: Register";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} <h1>User register</h1>
@if (ViewBag.Registered != null && ViewBag.Registered)
{
<p>User "@Model.UserName" has been created sucessfully!</p>
}
else
{
<p>Please input user name and password to register:</p>
using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorForModel()
<p>
<input type="submit" value="Register" /></p>
}
}

作为示例这里简单的收集用户名和密码,成功注册后给出提示,Html.ValidationSummary()显示发生的错误发生,比如用户名已经存在。我们可以在布局文件中创建一些链接关联到用户注册、登出:

...
<div>
@if (User.Identity.IsAuthenticated)
{
<p>Current user:@User.Identity.Name</p>
@Html.RouteLink("Logout",new {Controller="Account",Action="Logout",returnUrl=Request.Url.PathAndQuery})
}
else
{
<span>@Html.RouteLink("Login",new {Controller="Account",Action="Login",returnUrl=Request.Url.PathAndQuery})</span>
<span>@Html.ActionLink("Register","Register","Account")</span>
}
</div>
<div>
@if (User.IsInRole("Admin"))
{
@Html.ActionLink("Administrate", "Index", "Admin")
}
</div>
...

Universal provider

SQL membership provider要求使用完整安装的SQL server,使用到很多表和存储过程,对SQL server azure、SQL server compact都不支持,于是Universal provider出现了,最早于 2011年发布。我们可以在VS2012中使用Basic模板创建MVC4工程,工程被配置为默认使用Universal provider。我们也可以在nuget包管理器搜索“universal”,找到“Microsoft ASP.NET universal provider”安装,安装工具修改web.config配置DefaultMembershipProvider作为默认的provider;配置EntityFramework,universal provider使用EntityFramework完成数据库的读写;创建一个SQL express的数据库和连接字符串供universal provider使用。下面是web.config的部分内容:

...
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
...
<profile defaultProvider="DefaultProfileProvider">
<providers>
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" passwordFormat="Hashed" passwordStrengthRegularExpression="" />
</providers>
</membership>
<roleManager defaultProvider="DefaultRoleProvider">
<providers>
<add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</roleManager>
<!--
If you are deploying to a cloud environment that has multiple web server instances,
you should change session state mode from "InProc" to "Custom". In addition,
change the connection string named "DefaultConnection" to connect to an instance
of SQL Server (including SQL Azure and SQL Compact) instead of to SQL Server Express.
-->
<sessionState mode="InProc" customProvider="DefaultSessionProvider">
<providers>
<add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
</providers>
</sessionState>
.... <entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework> ...

打开asp.net配置工具,可以看到成员资格提供程序有AspNetSqlMembershipProvider和DefaultMembershipProvider供选择,前者就是sql membership provider,我们我们这时选择它,配置工具会把membership改为:

<membership>

defaultProvider特性被删除,不带任何的特性,这需要特别注意。

查看Universal provider生成的数据库,它只包含Users、Roles、Profiles、Memberships、UsersInRoles、Applications几个表,而且没有任何的存储过程,确实很多程度上简化了数据库模型,不再使用存储过程操作数据,因此支持的SQL服务类型也更多。nuget包安装工具为我们自动创建了一个数据库,如果我们要使用原有的数据库该怎么办呢?我们只需要改动相应的连接字符串,编译后启动asp.net配置工具,它会在我们原有的数据库中创建上面的几个表。

SQL membership provider一节示例的的控制器/视图我们不需要任何改动都可以在切换成universal provider后正常运行,对Membership方法的调用在MVC内部转由System.Web.Providers.DefaultProfileProvider,对我们写程序讲没有任何不同。这样讲似乎universal provider没有带来太多的好处,实际上随着数据库结构的简化,对我们扩展profile等有很大的便利,这里就不再深入讨论。

Simple provider

simple provider在VS 2010 SP1中随Webmatrix发布,和universal provider一样使用entrity framework操作用户信息数据库,但是数据库的结构更为简单也可以更为灵活的配置。在VS2012中我们使用Internet模板创建MVC4的工程,工程被配置为使用simple provider。web.config中只有<authentication mode="Forms">,不再包含membership provider的信息,membership的处理直接在控制器中使用WebMatrix.WebData.WebSecurity处理。Internet模板创建了具备完整用户功能的代码,这里不一一列出。

Internet模板创建一个名为InitializeSimpleMembershipAttribute的过滤器,它在每次应用程序启动时调用一次:

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

这个方法使初始化用户信息的数据库连接,DefaultConnection为数据库的连接字符串,Userpofile为表名称,UserId和UserName分别为用户ID和用户名称在表中的字段名称,也就是说我们只需要一个最简单的有用户ID和名称两个字段的表就可以了,这个表可以在任何数据库中,这是可以动态设置的,所以asp.net的配置工具不能用于配置simple provider。

Internet模板创建Account控制器,包含众多action方法用于提供用户注册、登录、登出、修改密码,基本上都是调用WebSecurity的相关方法来实现的,比如登录调用的是WebSecurity.Login()。在Internet模板的基础上,我们可以很方便的自定义profile、roles等,这里也不再深入,已经有一篇很好的文章讲解simple provider如何工作,可以参见http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx

MVC5已随VS2013在2013十月发布,相对于MVC4有了很多的变化,包括这里所讲的安全认证。就以本文结束MVC4,开始MVC5之旅。

ASP.NET MVC 4 (十三) 基于表单的身份验证的更多相关文章

  1. 【FBA】SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用

    //http://www.cnblogs.com/OceanEyes/p/custom-provider-in-sharepoint-2013-fba-authentication.html 由于项目 ...

  2. [FBA]SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用

    //http://tech.ddvip.com/2014-05/1401197453210723.html 由于项目的需要,登录SharePoint Application的用户将从一个统一平台中获取 ...

  3. SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用

    由于项目的需要,登录SharePoint Application的用户将从一个统一平台中获取,而不是从Domain中获取,所以需要对SharePoint Application的身份验证(Claims ...

  4. asp.net 如何配置authentication,完成基于表单的身份验证

    步骤一: 在根目录下的web.config中加入: <system.web> <authentication mode="Forms">           ...

  5. 基于表单的身份验证(FBA)

    https://technet.microsoft.com/zh-cn/library/ee806890(office.15).aspx http://www.tuicool.com/articles ...

  6. JavaWeb应用中的身份验证(声明式)——基于表单的身份认证

    容器管理安全最普遍的类型建立在基于表单的身份验证方式上. 通过这样的方式,server自己主动将尚未验证的用户重定向到一个HTML表单.检查他们的username和password,决定他们属于哪个角 ...

  7. SharePoint 2013 配置基于表单的身份认证

    前 言 这里简单介绍一下为SharePoint 2013 配置基于表单的身份认证,简单的说,就是用Net提供的工具创建数据库,然后配置SharePoint 管理中心.STS服务.Web应用程序的三处w ...

  8. 在Tomcat中采用基于表单的安全验证

    .概述   (1)基于表单的验证 基于From的安全认证可以通过TomcatServer对Form表单中所提供的数据进行验证,基于表单的验证使系统开发者可以自定义用户的登陆页面和报错页面.这种验证方法 ...

  9. 如何在.Net Core MVC中为动态表单开启客户端验证

    非Core中的请参照: MVC的验证 jquery.validate.unobtrusive mvc验证jquery.unobtrusive-ajax 参照向动态表单增加验证 页面引入相关JS: &l ...

随机推荐

  1. linux 内核 内存管理 slub算法 (一) 原理

    http://blog.csdn.net/lukuen/article/details/6935068

  2. android: 动态加载碎片布局的技巧

    虽然动态添加碎片的功能很强大,可以解决很多实际开发中的问题,但是它毕竟只是在 一个布局文件中进行一些添加和替换操作.如果程序能够根据设备的分辨率或屏幕大小在运 行时来决定加载哪个布局,那我们可发挥的空 ...

  3. Spring boot设置文件上传大小限制

    原文:https://blog.csdn.net/lizhangyong1989/article/details/78586421 Spring boot1.0版本的application.prope ...

  4. 最近无意中看到一个讲解spring mvc的系列,从源码的角度讲解,特记录下来,供以后反复学习

    SpringMVC深度探险(一) —— SpringMVC前传 SpringMVC深度探险(二) —— SpringMVC概览 SpringMVC深度探险(三) —— DispatcherServle ...

  5. Spring Date JPA 更新部分字段

    在Spring Data JPA 中,新增和更新操作都是用save()的方式进行,JPA是通过什么方法来知道我们是要进行insert还是update呢? 经过测试,JPA对程序调用的save()方法判 ...

  6. linux设置预留端口号,防止监听端口被占用 ip_local_reserved_ports

    1. 背景 linux服务器启动时,会对指定的端口进行监听bind,如果同一个机器上这个端口已经被使用,则监听失败,程序无法启动. linux客户端连接服务器accept时,系统会分配本地临时端口用于 ...

  7. 浅谈压缩感知(二十一):压缩感知重构算法之正交匹配追踪(OMP)

    主要内容: OMP的算法流程 OMP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 稀疏度K与重构成功概率关系的实验与结果 一.OMP的算法流程 二.OMP的MATL ...

  8. ubuntu 安装SSH并设置免密码登录

    cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhost ssh-keygen -t rsa # 会有提示,都按回车就可以 cat ./id_rsa.pub >> . ...

  9. javascript验证QQ号、邮箱和手机号码

    //验证QQ号码5-11位 function isQQ(qq) { var filter = /^\s*[.0-9]{5,11}\s*$/; if (!filter.test(qq)) { retur ...

  10. 五种常见的ASP.NET安全缺陷

    保证应用程序的安全应当从编写第一行代码的时候开始做起,原因很简单,随着应用规模的发展,修补安全漏洞所需的代价也随之快速增长.根据IBM的系统科学协会(SystemsSciencesInstitute) ...