ASP.NET MVC 4 (十三) 基于表单的身份验证

2020-12-13 02:17

阅读:409

标签:style   blog   class   code   java   tar   

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

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

soscw.com,搜素材
... 
authentication mode="Forms"> 
  forms loginUrl="~/Account/Login" timeout="2880"> 
    credentials passwordFormat="Clear"> 
      user name="admin" password="secret" /> 
    credentials> 
  forms> 
authentication> 
... 
soscw.com,搜素材

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

soscw.com,搜素材
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; }
    }
}
soscw.com,搜素材

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

soscw.com,搜素材
@model SportsStore.WebUI.Models.LoginViewModel

@{
    ViewBag.Title = "Admin: Log In";
    Layout = "~/Views/Shared/_AdminLayout.cshtml";
}

h1>Log Inh1>

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>
}
soscw.com,搜素材

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

soscw.com,搜素材
[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();
            }
        }
soscw.com,搜素材

调用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中添加“”。配置工具仍然没有在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,让它使用上面的数据库连接:

soscw.com,搜素材
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>
soscw.com,搜素材

再次打开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认证的配置,我们继续完善上面的例子,从控制器开始:

soscw.com,搜素材
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);
        }
    }
}
soscw.com,搜素材

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

soscw.com,搜素材
@model SportsStore.WebUI.Models.LoginViewModel

@{
    ViewBag.Title = "User: Register";
    Layout = "~/Views/Shared/_AdminLayout.cshtml";
}

h1>User registerh1>
@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>
    }
}
soscw.com,搜素材

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

soscw.com,搜素材
...
div>
            @if (User.Identity.IsAuthenticated)
            {
                p>Current user:@User.Identity.Namep>
                @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>
...
soscw.com,搜素材

 

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的部分内容:

soscw.com,搜素材
...
configSections>
    
    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>
    
    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>

...
soscw.com,搜素材

打开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中只有,不再包含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 (十三) 基于表单的身份验证,搜素材,soscw.com

ASP.NET MVC 4 (十三) 基于表单的身份验证

标签:style   blog   class   code   java   tar   

原文地址:http://www.cnblogs.com/duanshuiliu/p/3715703.html


评论


亲,登录后才可以留言!