【Pro ASP.NET MVC 3 Framework】.学习笔记.9.SportsStore:Securing the Administration Features

1 设置表单身份认证

因为ASP.NET MVC基于ASP.NET平台的核心,所以我们可以使用ASP.NET Form的身份认证,这是保持用户登录轨迹通用的方法。现在介绍最基本的配置。

在Web.config文件中,有这么一段

1 < authentication mode = " Forms " > 2 < forms loginUrl = " ~/Account/LogOn " timeout = " 2880 " /> 3 </ authentication >

表单身份认证自动地被空的模板或Internet程序模板MVC程序启用。当需要要认证时,loginUrl属性告诉ASP.NET,定向到哪个URL。在这里,会定向到/Account/Logon页面。timeout属性指定用户登陆后,过期时间。

表单身份认证主要可选的是Windows authentication,使用操作系统证书来识别用户。这在部署企业内网程序时很方便,所有的用户都在痛一个windows domain。但是不适用internet程序。

如果我们选择MVC Internet程序模板,VS会创建AccountController类和它的LogOn action方法。这个方法的实现,会使用核心的ASP.NET成员资格特性,来管理账号和密码。

简单地实现它,我们指定同意进入管理特性的账号密码。

1 < credentials passwordFormat = " Clear " > 2 < user name = " admin " password = " secret " /> 3 </ credentials >

我们决定简单点,将用户名和密码硬编码到Web.config文件。大多数网络程序使用表单身份验证,在数据库中存储用户的认证信息。

2 应用带过滤器的身份认证

MVC框架有个强大的特性叫做filters,这是一个可以应用给action方法或controller类的.NET特性。当请求被处理时,它采用附加的逻辑。不同类型的过滤器都可以使用,也可以创建自己的自定义顾虑器。当前我们感兴趣的是Authorize过滤器,默认身份验证过滤器。我们将它应用在AdminController类上。

1 [Authorize] 2 public class AdminController : Controller

当使用Authorize属性,不带任何参数时,如果用户已验证,它同意进入controller action方法。这意味着如果你已经验证,你可以自动地经过授权地使用administation特性。这里只有一组受限制的action方法和一个使用者,这对SportsStore很好。你会看到如何有选择地应用Authorize过滤器,来区分authentication(被系统识别的)和 authorized(被允许访问给定的action方法)。

你可以应用过滤器给一个个体action方法或controller。当你应用一个过滤器给controller,它会应用给controller类中的每个action方法。我们给AdminController类使用了Authorize过滤器,它所有的action方法只对已验证的用户可用。

导航到/Admin/Index URL时,你会看到Authorize过滤器的效果。当试图访问Admin controller的Index action方法,MVC框架发现Authorize过滤器。因为你还没有被认证,被重定向到Web.config表单身份验证节点指定的URL:Account/LogOn。我们还没有创建Account controller,但是身份验证系统已经在工作。

3 创建身份验证提供者

使用表单身份认证特性,需要我们调用System.Web.Security.FormAuthentication类的两个静态方法:

  • Authenticate(认证,鉴定)方法,让我们验证用户提供的认证信息
  • SetAuthCookie方法,添加一个cookie到response到浏览器,所以用户不需要在他们每次请求时都被认证。

在action方法中调用静态方法的问题是,它使得controller的单元测试变得困难。Mocking框架,如Moq,只能mock成员的实例。问题的出现时因为FormAuthentication类被MVC设计为,早于单元测试运行。最好的解决方法,使用一个带静态方法的接口,解耦controller。这样做,附加的好处是,它适用于广泛的MVC设计模式,并使得它在以后易于切换成不同的身份认证系统。

我们从定义身份认证提供者接口开始。在Infrastructure文件夹创建新的Abstract文件夹,在它里面添加IAuthProvider接口。

1 public interface IAuthProvider 2 { 3 bool Authenticate( string username, string password); 4 }

然后创建它的实现,来扮演FormsAuthentication类的静态方法的包装。在Infrastructure文件夹中,创建另一个新文件夹Concrete,在它里面新建一个FormsAuthProvider类。

1 public class FormsAuthProvider:IAuthProvider 2 { 3 public bool Authenticate( string username, string password) 4 { 5 bool result = FormsAuthentication.Authenticate(username, password); 6 if (result){ 7 FormsAuthentication.SetAuthCookie(username, false ); 8 } 9 return result; 10 } 11 }

Auhtenticate模型的实现,调用我们想要留在controller外面的静态方法。最后一步是在NinjectControllerFactory类的AddBindings方法中注册FormAuthProvider。

1 ninjectKernel.Bind < IAuthProvider > ().To < FormsAuthProvider > ();

4 创建Account Controller

下一步的任务是创建Account controller和LogOn action方法。事实上,我们会创建两个版本的LogOn方法。第一个会渲染包含登录提示的视图。另一在用户提交他们的认证信息时,处理POST请求。

首先,我们要创建一个在controller和view之间传递的视图模型。在Models文件夹中创建新类LogOnViewModel:

1 public class LogOnViewModel 2 { 3 [Required] 4 public string UserName { get ; set ; } 5 6 [Required] 7 [DataType(DataType.Password)] 8 public string Password { get ; set ; } 9 }

使用data annotations指定这些属性是必须的,使用DateType属性告诉MVC框架Password属性显示。也许你会觉得用ViewBag传递数据给视图更好。然而,这时一个很好的实践,定义视图模型,让数据从controller传递到view,从模型绑定到action方法。这允许我们使用template view helpers更简单。

接着,创建一个AccountController

1 public class AccountController : Controller 2 { 3 // 4 // GET: /Account/ 5 IAuthProvider authProvider; 6 7 public AccountController(IAuthProvider auth) 8 { 9 authProvider = auth; 10 } 11 12 public ViewResult LogOn() 13 { 14 return View(); 15 } 16 17 [HttpPost] 18 public ActionResult LogOn(LogOnViewModel model, string returnUrl) 19 { 20 if (ModelState.IsValid) 21 { 22 if (authProvider.Authenticate(model.UserName, model.Password)) 23 { 24 return Redirect(returnUrl ?? Url.Action( " Index " , " Admin " )); 25 } 26 else 27 { 28 ModelState.AddModelError( "" , " Incorrect username or passord " ); 29 return View(); 30 } 31 } 32 else 33 { 34 return View(); 35 } 36 } 37 }

5 创建视图

为LogOn action方法创建LogOn强类型视图,模式为LogOnViewModel。

1 @model SportsStore.WebUI.Models.LogOnViewModel 2 3 @{ 4 ViewBag.Title = " Admin: Log In " ; 5 Layout = " ~/Views/Shared/_AdminLayout.cshtml " ; 6 } 7 8 < h1 > Log In </ h1 > 9 < p > Please log in to access the administrative area: </ p > 10 @using (Html.BeginForm()) 11 { 12 @Html.ValidationSummary( true ) 13 @Html.EditorForModel() 14 < p >< input type = " submit " value = " Log in " /></ p > 15 }

DataType属性让MVC框架渲染Password属性诶HTML password-input元素,这意味着字符不可见。Required属性会强制使用客户端校验(需要引用required JavaScript的库文件).当偶们调用FormAuthentication.Authenticate方法,身份验证会在服务端执行。

一般,使用客户端校验很好,它不用从服务器加载东西,立即返回。然而,你不能试图执行身份校验在客户端,因为代表性地发送验证信息到客户端,他们可以用来检查可以进入的username和password,或者最少信任客户端发回的通过身份验证的报告。身份验证必须总是在服务端执行。

当我们接收到错误的认证信息,我们在ModelState中添加作物信息,并将它渲染到视图。我们的消息显示在验证汇总区域,通过调用Html.ValidationSummary helper方法。我们调用它,使用一个bool参数值true。这样做它不在属性元素旁边显示验证信息,只在汇总区域显示。

5.1 身份验证的单元测试

要测试两个特性,当用户提供可用的验证信息时通过验证,当用户提供不可用的信息时不通过验证。创建IAuthProvider接口的mock实现,并检查controller LogOn方法的原生结果和类型。

1 [TestMethod] 2 public void Can_Login_With_Valid_Credentials() 3 { 4 // Arrange - create a mock authentication provider 5 Mock < IAuthProvider > mock = new Mock < IAuthProvider > (); 6 mock.Setup(m => m.Authenticate( " admin " , " secret " )).Returns( true ); 7 8 // Arrange - create the view model 9 LogOnViewModel model = new LogOnViewModel 10 { 11 UserName = " admin " , 12 Password = " secret " 13 }; 14 15 // Arrange - create the controller 16 AccountController target = new AccountController(mock.Object); 17 18 // Act - authenticate using valid credentials 19 ActionResult result = target.LogOn(model, " /MyURL " ); 20 21 // Assert 22 Assert.IsInstanceOfType(result, typeof (RedirectResult)); 23 Assert.AreEqual( " /MyURL " , ((RedirectResult)result).Url); 24 25 } 26 27 [TestMethod] 28 public void Cannot_Login_With_Invalid_Credentials() 29 { 30 // Arrange - create a mock authentication provider 31 Mock < IAuthProvider > mock = new Mock < IAuthProvider > (); 32 mock.Setup(m => m.Authenticate( " badUser " , " badPass " )).Returns( false ); 33 34 // Arrange - create the view model 35 LogOnViewModel model = new LogOnViewModel 36 { 37 UserName = " badUser " , 38 Password = " badPass " 39 }; 40 41 // Arrange - create the controller 42 AccountController target = new AccountController(mock.Object); 43 44 // Act - authenticate using valid credentials 45 ActionResult result = target.LogOn(model, " /MyURL " ); 46 47 // Assert 48 Assert.IsInstanceOfType(result, typeof (ViewResult)); 49 Assert.IsFalse(((ViewResult)result).ViewData.ModelState.IsValid); 50 }

最好使用SSL(Secure Sockets Layer),请求验证的验证信息和身份验证cookie(为随后识别客户)会通过secure connection传送。

你可能感兴趣的:(framework)