ASP.NET MVC 4 (十三) 基于表单的身份验证
在前面的章節(jié)中我們知道可以在MVC應(yīng)用程序中使用[Authorize]特性來(lái)限制用戶(hù)對(duì)某些網(wǎng)址(控制器/控制器方法)的訪(fǎng)問(wèn),但這都是在對(duì)用戶(hù)認(rèn)證之后,而用戶(hù)的認(rèn)證則依然是使用ASP.NET平臺(tái)的認(rèn)證機(jī)制。
ASP.NET提供Windows和Forms兩種身份驗(yàn)證,前者主要用于Intranet上域環(huán)境內(nèi),后者則更多的應(yīng)用于Internet,這里我們只討論后者。先從最簡(jiǎn)單的例子開(kāi)始,我們?cè)趙eb.config中配置Forms認(rèn)證方式:
... <authentication mode="Forms"> <forms loginUrl="~/Account/Login" timeout="2880"> <credentials passwordFormat="Clear"> <user name="admin" password="secret" /> </credentials> </forms> </authentication> ...
這里設(shè)置認(rèn)證方式為Forms,用戶(hù)登錄的地址為~/Account/Login,我們用最簡(jiǎn)單的方式創(chuàng)建用戶(hù)信息,在credentials節(jié)中直接設(shè)置用戶(hù)名稱(chēng)/密碼。在創(chuàng)建頁(yè)面之前我們先創(chuàng)建收集用戶(hù)名和密碼Model類(lèi):
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; }}
} 創(chuàng)建一個(gè)視圖來(lái)收集用戶(hù)名和信息:
@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中處理用戶(hù)提交的用戶(hù)名和密碼完成用戶(hù)認(rèn)證:
[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();}} 調(diào)用FormsAuthentication.Authenticate()對(duì)用戶(hù)名和密碼驗(yàn)證,如何驗(yàn)證成功,調(diào)用FormsAuthentication.SetAuthCookie()設(shè)置用戶(hù)驗(yàn)證的cookie并在響應(yīng)中返回,在cookie過(guò)期之前用戶(hù)再次訪(fǎng)問(wèn)時(shí)不再要求登錄。
以上就是最簡(jiǎn)單的Forms身份驗(yàn)證過(guò)程,但實(shí)際的Internet應(yīng)用用戶(hù)的信息一般存儲(chǔ)在數(shù)據(jù)庫(kù)中,通過(guò)Membership provider利用數(shù)據(jù)庫(kù)中的信息對(duì)用戶(hù)驗(yàn)證,MVC4中微軟為我們提供SQL membership provider、Universal membership provider和Simple membership provider,下面來(lái)看看如何具體如何使用它們。
SQL membership provider
在.NET 2.0中SQL membership provider就已經(jīng)存在了,在visual studio 2012中使用empty模板創(chuàng)建一個(gè)MVC4的工程,web.config你不會(huì)看到任何membership provider相關(guān)的信息,默認(rèn)使用的是Windows認(rèn)證。在VS的Project菜單下打開(kāi)Asp.net configurtion工具(在打開(kāi)配置工具前記得編譯下工程,否則會(huì)提示“選定的數(shù)據(jù)存儲(chǔ)區(qū)出現(xiàn)問(wèn)題”),在“安全”標(biāo)簽頁(yè)面點(diǎn)擊“選擇身份驗(yàn)證類(lèi)型”,配置工具詢(xún)問(wèn)“用戶(hù)如何訪(fǎng)問(wèn)您的站點(diǎn)?”,選擇“通過(guò)Internet”,點(diǎn)擊“完成”后配置工具將自動(dòng)在web.config中添加“<authentication mode="Forms" />”。配置工具仍然沒(méi)有在web.config添加任何membership provider的信息,但是我們轉(zhuǎn)到配置工具的“提供程序頁(yè)面”,可以看到看到默認(rèn)選中的是AspNetSqlMembershipProvider,同時(shí)配置工具會(huì)在工程的app_data目錄下創(chuàng)建一個(gè)名為ASPNETDB.MDF的數(shù)據(jù)庫(kù),這是一個(gè)sql express的數(shù)據(jù)庫(kù),visual studio 2012中不能直接打開(kāi)(VS用的是localdb),可以在SQL管理工具中附加到SQL EXPRESS的服務(wù)實(shí)例來(lái)查看。打開(kāi)數(shù)據(jù)庫(kù)可以看到數(shù)據(jù)庫(kù)中添加了很多“aspnet_”為前綴的表和存儲(chǔ)過(guò)程,這些都是SqlMembershipProvider需要的。
如果我們要使用自建的數(shù)據(jù)庫(kù)來(lái)保存用戶(hù)信息改如何操作呢?我們?cè)赟olution exploer中點(diǎn)擊App_Start目錄,右鍵菜單中選擇添加->添加項(xiàng)目->SQL數(shù)據(jù)庫(kù)創(chuàng)建一個(gè)localdb的數(shù)據(jù)庫(kù),添加相應(yīng)的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,讓它使用上面的數(shù)據(jù)庫(kù)連接:
<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>
再次打開(kāi)asp.net配置工具轉(zhuǎn)到安全界面會(huì)提示錯(cuò)誤“Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'”,配置工具試圖調(diào)用相關(guān)的存儲(chǔ)過(guò)程,但是數(shù)據(jù)庫(kù)是我們手工創(chuàng)建的,不包含這些過(guò)程和數(shù)據(jù)表。我們可以使用aspnet_regsql.exe工具在我們的數(shù)據(jù)庫(kù)中創(chuàng)建相關(guān)表和數(shù)據(jù),C:\Windows\Microsoft.NET\Framework64\v4.0.30319和C:\Windows\Microsoft.NET\Framework64\v2.0.50727都有這個(gè)工具,沒(méi)有細(xì)究?jī)蓚€(gè)版本的不同,這里使用.NET 4.0的版本。在aspnet_regsql工具選擇服務(wù)器為“(localdb)\v11.0”,數(shù)據(jù)庫(kù)列表中如果找不到新建的數(shù)據(jù)庫(kù),可以事先在sql manage studio中連接到服務(wù)引擎“(localdb)\v11.0”后附加該數(shù)據(jù)庫(kù)(aspnet_reqsql也支持使用連接字符串作為參數(shù),參見(jiàn)http://msdn.microsoft.com/en-us/library/ms229862(v=vs.100).aspx)。完成上述操作后,asp.net配置工具就可以在我們的數(shù)據(jù)庫(kù)中創(chuàng)建管理用戶(hù)了。
準(zhǔn)備好Forms認(rèn)證的配置,我們繼續(xù)完善上面的例子,從控制器開(kāi)始:
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);}}
} 在用戶(hù)登錄時(shí)不再使用FormsAuthentication.Authenticate()認(rèn)證用戶(hù),它僅讀取web.config中credentials節(jié)的內(nèi)容,我們需要改用Membership.ValidateUser()對(duì)用戶(hù)密碼校驗(yàn)。調(diào)用FormsAuthentication.SignOut()登出用戶(hù),它清除認(rèn)證相關(guān)的cookie。Register() action用于創(chuàng)建用戶(hù),它調(diào)用Membership.CreateUser()創(chuàng)建一個(gè)用戶(hù)保存到數(shù)據(jù)庫(kù)中,對(duì)應(yīng)的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>}
} 作為示例這里簡(jiǎn)單的收集用戶(hù)名和密碼,成功注冊(cè)后給出提示,Html.ValidationSummary()顯示發(fā)生的錯(cuò)誤發(fā)生,比如用戶(hù)名已經(jīng)存在。我們可以在布局文件中創(chuàng)建一些鏈接關(guān)聯(lián)到用戶(hù)注冊(cè)、登出:
...
<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,使用到很多表和存儲(chǔ)過(guò)程,對(duì)SQL server azure、SQL server compact都不支持,于是Universal provider出現(xiàn)了,最早于 2011年發(fā)布。我們可以在VS2012中使用Basic模板創(chuàng)建MVC4工程,工程被配置為默認(rèn)使用Universal provider。我們也可以在nuget包管理器搜索“universal”,找到“Microsoft ASP.NET universal provider”安裝,安裝工具修改web.config配置DefaultMembershipProvider作為默認(rèn)的provider;配置EntityFramework,universal provider使用EntityFramework完成數(shù)據(jù)庫(kù)的讀寫(xiě);創(chuàng)建一個(gè)SQL express的數(shù)據(jù)庫(kù)和連接字符串供universal provider使用。下面是web.config的部分內(nèi)容:
... <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 instanceof 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>...
打開(kāi)asp.net配置工具,可以看到成員資格提供程序有AspNetSqlMembershipProvider和DefaultMembershipProvider供選擇,前者就是sql membership provider,我們我們這時(shí)選擇它,配置工具會(huì)把membership改為:
<membership>
defaultProvider特性被刪除,不帶任何的特性,這需要特別注意。
查看Universal provider生成的數(shù)據(jù)庫(kù),它只包含Users、Roles、Profiles、Memberships、UsersInRoles、Applications幾個(gè)表,而且沒(méi)有任何的存儲(chǔ)過(guò)程,確實(shí)很多程度上簡(jiǎn)化了數(shù)據(jù)庫(kù)模型,不再使用存儲(chǔ)過(guò)程操作數(shù)據(jù),因此支持的SQL服務(wù)類(lèi)型也更多。nuget包安裝工具為我們自動(dòng)創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù),如果我們要使用原有的數(shù)據(jù)庫(kù)該怎么辦呢?我們只需要改動(dòng)相應(yīng)的連接字符串,編譯后啟動(dòng)asp.net配置工具,它會(huì)在我們?cè)械臄?shù)據(jù)庫(kù)中創(chuàng)建上面的幾個(gè)表。
SQL membership provider一節(jié)示例的的控制器/視圖我們不需要任何改動(dòng)都可以在切換成universal provider后正常運(yùn)行,對(duì)Membership方法的調(diào)用在MVC內(nèi)部轉(zhuǎn)由System.Web.Providers.DefaultProfileProvider,對(duì)我們寫(xiě)程序講沒(méi)有任何不同。這樣講似乎universal provider沒(méi)有帶來(lái)太多的好處,實(shí)際上隨著數(shù)據(jù)庫(kù)結(jié)構(gòu)的簡(jiǎn)化,對(duì)我們擴(kuò)展profile等有很大的便利,這里就不再深入討論。
Simple provider
simple provider在VS 2010 SP1中隨Webmatrix發(fā)布,和universal provider一樣使用entrity framework操作用戶(hù)信息數(shù)據(jù)庫(kù),但是數(shù)據(jù)庫(kù)的結(jié)構(gòu)更為簡(jiǎn)單也可以更為靈活的配置。在VS2012中我們使用Internet模板創(chuàng)建MVC4的工程,工程被配置為使用simple provider。web.config中只有<authentication mode="Forms">,不再包含membership provider的信息,membership的處理直接在控制器中使用WebMatrix.WebData.WebSecurity處理。Internet模板創(chuàng)建了具備完整用戶(hù)功能的代碼,這里不一一列出。
Internet模板創(chuàng)建一個(gè)名為InitializeSimpleMembershipAttribute的過(guò)濾器,它在每次應(yīng)用程序啟動(dòng)時(shí)調(diào)用一次:
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); 這個(gè)方法使初始化用戶(hù)信息的數(shù)據(jù)庫(kù)連接,DefaultConnection為數(shù)據(jù)庫(kù)的連接字符串,Userpofile為表名稱(chēng),UserId和UserName分別為用戶(hù)ID和用戶(hù)名稱(chēng)在表中的字段名稱(chēng),也就是說(shuō)我們只需要一個(gè)最簡(jiǎn)單的有用戶(hù)ID和名稱(chēng)兩個(gè)字段的表就可以了,這個(gè)表可以在任何數(shù)據(jù)庫(kù)中,這是可以動(dòng)態(tài)設(shè)置的,所以asp.net的配置工具不能用于配置simple provider。
Internet模板創(chuàng)建Account控制器,包含眾多action方法用于提供用戶(hù)注冊(cè)、登錄、登出、修改密碼,基本上都是調(diào)用WebSecurity的相關(guān)方法來(lái)實(shí)現(xiàn)的,比如登錄調(diào)用的是WebSecurity.Login()。在Internet模板的基礎(chǔ)上,我們可以很方便的自定義profile、roles等,這里也不再深入,已經(jīng)有一篇很好的文章講解simple provider如何工作,可以參見(jiàn)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十月發(fā)布,相對(duì)于MVC4有了很多的變化,包括這里所講的安全認(rèn)證。就以本文結(jié)束MVC4,開(kāi)始MVC5之旅。
轉(zhuǎn)載于:https://www.cnblogs.com/GoogleGetZ/p/5835823.html
總結(jié)
以上是生活随笔為你收集整理的ASP.NET MVC 4 (十三) 基于表单的身份验证的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 黄体酮多少钱啊?
- 下一篇: 将 instance 连接到 first