ASP.NET MVC 4 (十三) 基于表单的身份验证
在前面的章节中我们知道可以在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 (十三) 基于表单的身份验证的更多相关文章
- 【FBA】SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用
//http://www.cnblogs.com/OceanEyes/p/custom-provider-in-sharepoint-2013-fba-authentication.html 由于项目 ...
- [FBA]SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用
//http://tech.ddvip.com/2014-05/1401197453210723.html 由于项目的需要,登录SharePoint Application的用户将从一个统一平台中获取 ...
- SharePoint 2013自定义Providers在基于表单的身份验证(Forms-Based-Authentication)中的应用
由于项目的需要,登录SharePoint Application的用户将从一个统一平台中获取,而不是从Domain中获取,所以需要对SharePoint Application的身份验证(Claims ...
- asp.net 如何配置authentication,完成基于表单的身份验证
步骤一: 在根目录下的web.config中加入: <system.web> <authentication mode="Forms"> ...
- 基于表单的身份验证(FBA)
https://technet.microsoft.com/zh-cn/library/ee806890(office.15).aspx http://www.tuicool.com/articles ...
- JavaWeb应用中的身份验证(声明式)——基于表单的身份认证
容器管理安全最普遍的类型建立在基于表单的身份验证方式上. 通过这样的方式,server自己主动将尚未验证的用户重定向到一个HTML表单.检查他们的username和password,决定他们属于哪个角 ...
- SharePoint 2013 配置基于表单的身份认证
前 言 这里简单介绍一下为SharePoint 2013 配置基于表单的身份认证,简单的说,就是用Net提供的工具创建数据库,然后配置SharePoint 管理中心.STS服务.Web应用程序的三处w ...
- 在Tomcat中采用基于表单的安全验证
.概述 (1)基于表单的验证 基于From的安全认证可以通过TomcatServer对Form表单中所提供的数据进行验证,基于表单的验证使系统开发者可以自定义用户的登陆页面和报错页面.这种验证方法 ...
- 如何在.Net Core MVC中为动态表单开启客户端验证
非Core中的请参照: MVC的验证 jquery.validate.unobtrusive mvc验证jquery.unobtrusive-ajax 参照向动态表单增加验证 页面引入相关JS: &l ...
随机推荐
- 怎么把html页面中共用的底部代码做成共享模块
问: 很多时候,我们在设计网站时会发现,站内每一个页面的header跟footer其实都是一样的,如果每个页面都写header跟footer就会显示代码冗余而且维护也不方便, 这时候最好的做法就是把相 ...
- win2008R2管理员密码修改文档
场景:忘记了win2008R2服务器的管理员密码.解决办法:1. 制作一个U盘启动盘:2. 系统通过U盘启动进入WINpe系统3. 在知道Win2008安装位置的情况下:查找C:\windows\sy ...
- .Net性能的方方面面(必看官方经典)
更多性能提高相关文章,必看 https://msdn.microsoft.com/en-us/library/hh917314.aspx Chapter 1 - Fundamentals of Eng ...
- Revit API根据链接文件开洞
开洞信息数据: ]); ; ; ; ; ...
- Java和.Net在做BS结构项目的比较
渊源: Java的J2EE在1999年形成了其成熟的架构,并且到今天已经有相当成熟的经过检验的企业应用系统.而.Net究其渊源是源自微软以前开发企业应用程序的平台DNA(DistributedNetw ...
- Go语言之高级篇beego框架之cookie与session
1.cookie的用法 this.Ctx.SetCookie("name", name, maxage, "/") this.Ctx.SetCookie(&qu ...
- CentOS7 下 keepalived 的安装和配置
安装前准备:yum -y install gcc gcc-c++ autoconf automake make yum -y install zlib zlib-devel openssl opens ...
- 【Matplotlib】数据可视化实例分析
数据可视化实例分析 作者:白宁超 2017年7月19日09:09:07 摘要:数据可视化主要旨在借助于图形化手段,清晰有效地传达与沟通信息.但是,这并不就意味着数据可视化就一定因为要实现其功能用途而令 ...
- ORA-19602: cannot backup or copy active file in NOARCHIVELOG mode
备份数据库,报错如下 RMAN> backup database; Starting backup at -JAN- allocated channel: ORA_DISK_1 channel ...
- git+gitolite如何实现权限控制
前言 首先说明一下,这还是本人第一次写这类文章,如有不妥,多多见谅. 基本情况 因为现在公司的人不是很多,但是还对代码有着严格的管控,所以采用了gitolite的管理方式 其实正常来讲,这种权限的把控 ...