• ASP.NET MVC 3.0(十): MVC 3.0 使用 Forms身份验证


    ASP.NET MVC 3.0(一): MVC 3.0 的新特性 摘要 

    ASP.NET MVC 3.0(二): MVC的概念及MVC 3.0开发环境

    ASP.NET MVC 3.0(三): 初识MVC的Url映射潜规则Routing

    ASP.NET MVC 3.0(四): 我要MVC潜规则之配置Routing

    ASP.NET MVC 3.0(五): 入手Controller/Action

    ASP.NET MVC 3.0(六): MVC 3.0创建你的View视图

    ASP.NET MVC 3.0(七): MVC 3.0 新的Razor引擎

    ASP.NET MVC 3.0(八): MVC 3.0 传递和保存你的Model

    ASP.NET MVC 3.0(九): MVC 3.0 验证你的Model

    ASP.NET MVC 3.0(十): MVC 3.0 使用 Forms身份验证

    ASP.NET MVC 3.0(十一): MVC 3.0 使用筛选器

    ASP.NET MVC 3.0(十二): MVC 3.0 使用自定义的Html控件

    ASP.NET MVC 3.0(十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击

    ASP.NET MVC 3.0(十四): MVC 3.0 实例系列之创建数据表格

    ASP.NET MVC 3.0(十五): MVC 3.0 实例系列之表格的排序

    ASP.NET MVC 3.0(十六): MVC 3.0 实例系列之表格数据的分页

    ASP.NET MVC 3.0(十七): MVC 3.0 实例之表格中数据的筛选

    ASP.NET MVC 3.0(十八): MVC 3.0 实例系列之表格中合并排序、分页和筛选

    ASP.NET MVC 3.0(十九): MVC 3.0 实例之使用开源控件实现表格排序和分页

    概述

     

    许多 Web 应用程序都要求能够限制对某些资源(例如特定页面)的访问,以确保只有经过身份验证的用户才能访问这些资源。 ASP.NET MVC 的默认 Web 应用程序项目模板提供了一个控制器以及一些数据模型和视图,您可使用这些组件为应用程序添加 ASP.NET 窗体身份验证功能。 借助该内置功能,用户可以注册、登录和注销,以及更改自己的密码。 对于许多应用程序,此功能可提供足够的用户身份验证级别。

    页面控件引用

    在MVC 3.0的项目模板里面的Shared文件夹中,我们可以看到名为_LogOnPartial.cshtml的页面,打开页面代码

     
    @if(Request.IsAuthenticated) {
    <text>欢迎使用 <b>@Context.User.Identity.Name</b>!
    [ @Html.ActionLink("注销", "LogOff", "User") ]
    </text>
    }
    else {
    @:[ @Html.ActionLink("登录", "LogOn", "User") ]
    }
     

    我们可以看到该页面只是根据获取系统中的用户登录名,<b>@Context.User.Identity.Name</b>判断是登录还是注销的。

    然后我们看看在什么地方用到了该页面?

    MVC 3.0的项目模板的Shared文件夹中,我们在_Layout.cshtml页面中找到了,这个页面就是项目的整个母版页,相当于.Master页面。

    代码

    <div id="logindisplay">
    @Html.Partial("_LogOnPartial")
    </div>

    这样简单的引用就可以将其他页面作为一个控件引用到该页面来。

    实现登录

    在前面的判断用户登录名时,我们可以看到有如下代码

    @:[ @Html.ActionLink("登录", "LogOn", "User") ]

    该代码指定登录在UserController的LogOn方法。

    创建用户Model类

     
    publicclass User
    {
    publicint ID { get; set; }

    [DisplayName(
    "姓名")]
    [Required(ErrorMessage
    ="姓名不能为空")]
    publicstring Name { get; set; }

    [DisplayName(
    "密码")]
    [Required]
    [DataType(DataType.Password)]
    publicstring Password { get; set; }

    [Display(Name
    ="记住我?")]
    publicbool RememberMe { get; set; }

    public Roles Roles { get; set; }

    }
     

    创建角色Model类,便于后面页面角色的管理

    代码:

    publicclass Roles
    {
    publicint ID { get; set; }
    publicstring Name { get; set; }
    }

    当前一个用户对应一个角色,不是一对多。后面有空会做个例子出来。

    所以我们创建UserController在UserController添加LogOn方法

    代码

     
    // **************************************
    // URL: /User/LogOn
    // **************************************

    public ActionResult LogOn()
    {
    return View();
    }
     

    创建视图

    代码

     
    @model MvcApplication.Models.User
    @{
    ViewBag.Title = "用户登录";
    Layout = "~/Views/Shared/_Layout.cshtml";
    }
    <h2>
    用户登录
    </h2>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    @using (Html.BeginForm())
    {
    @Html.ValidationSummary(true)
    <fieldset>
    <legend>登录信息</legend>
    <div class="editor-label">
    @Html.LabelFor(model => model.Name)
    </div>
    <div class="editor-field">
    @Html.EditorFor(model => model.Name)
    @Html.ValidationMessageFor(model => model.Name)
    </div>
    <div class="editor-label">
    @Html.LabelFor(model => model.Password)
    </div>
    <div class="editor-field">
    @Html.EditorFor(model => model.Password)
    @Html.ValidationMessageFor(model => model.Password)
    </div>
    <div class="editor-label">
    @Html.LabelFor(model => model.RememberMe)
    </div>
    <div class="editor-field">
    @Html.EditorFor(model => model.RememberMe)
    @Html.ValidationMessageFor(model => model.RememberMe)
    </div>
    <p>
    <input type="submit" value="登录"/>
    </p>
    </fieldset>
    }
    <div>
    @Html.ActionLink("返回主页", "Index")
    </div>
     

    有了界面,但是我们还没有对数据的操作

    业务逻辑接口

    创建一个名为IUserBusiness接口

     
    publicinterface IUserBusiness
    {
    ///<summary>
    /// 获取用户角色
    ///</summary>
    ///<param name="userName"></param>
    ///<returns></returns>
    Roles GetRoles(string userName);
    ///<summary>
    /// 根据用户名和密码获取用户信息
    ///</summary>
    ///<param name="name"></param>
    ///<param name="password"></param>
    ///<returns></returns>
    User GetByNameAndPassword(string name, string password);
    ///<summary>
    /// 登录
    ///</summary>
    ///<param name="userName"></param>
    ///<param name="createPersistentCookie"></param>
    void SignIn(string userName, bool createPersistentCookie);
    ///<summary>
    /// 注销
    ///</summary>
    void SignOut();
    }
     

    实现接口

     
    publicclass UserBusiness : IUserBusiness
    {
    ///<summary>
    /// 作为模拟的数据集
    ///</summary>
    privatestatic User[] UserList =new[]
    {
    new User{ ID =1, Name ="张三", Password ="111111", Roles =new Roles{ID=101,Name ="employee"}},
    new User{ ID =2, Name ="李四", Password ="111111", Roles =new Roles{ID =102,Name ="manager"}},
    new User{ ID =3, Name ="admin", Password ="admin", Roles =new Roles{ID =103,Name ="admin"}}
    };

    ///<summary>
    /// 根据用户名获取角色
    ///</summary>
    ///<param name="userName"></param>
    ///<returns></returns>
    public Roles GetRoles(string userName)
    {
    return UserList
    .Where(o
    => o.Name == userName)
    .Select(o
    => o.Roles)
    .FirstOrDefault();
    }

    ///<summary>
    /// 根据用户名和密码获取用户
    ///</summary>
    ///<param name="name"></param>
    ///<param name="password"></param>
    ///<returns></returns>
    public User GetByNameAndPassword(string name, string password)
    {
    return UserList
    .FirstOrDefault(u
    => u.Name == name && u.Password == password);
    }

    ///<summary>
    /// 登录
    ///</summary>
    ///<param name="userName"></param>
    ///<param name="createPersistentCookie"></param>
    publicvoid SignIn(string userName, bool createPersistentCookie)
    {
    if (String.IsNullOrEmpty(userName)) thrownew ArgumentException("值不能为 null 或为空。", "userName");



    FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }

    ///<summary>
    /// 注销
    ///</summary>
    publicvoid SignOut()
    {
    FormsAuthentication.SignOut();
    }
    }
     

    上面的角色获取和用户名密码获取都可以变化为数据库里面的数据。而登录只是把获取的数据添加到了cookies里面。注销亦然是使用了该机制。

    所以上面登录中的

    FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);

    完全可以换成

     
                        FormsAuthenticationTicket ticket =new FormsAuthenticationTicket(
    1,
    user.Name,
    DateTime.Now,
    DateTime.Now.Add(FormsAuthentication.Timeout),
    user.RememberMe,
    user.Name
    );
    HttpCookie cookie
    =new HttpCookie(
    FormsAuthentication.FormsCookieName,
    FormsAuthentication.Encrypt(ticket));
    Response.Cookies.Add(cookie);
     

    同样实现了登录的操作。

    方法调用

    我们在实现操作的时候要调用IUserBusiness接口中的相应方法来实现。

    所以参考微软的做法,重写了Initialize方法,把IUserBusiness接口作为一个属性在程序中使用。

    代码:

     
    public IUserBusiness UserBusiness { get; set; }

    protectedoverridevoid Initialize(RequestContext requestContext)
    {
    if (UserBusiness ==null) { UserBusiness =new UserBusiness(); }

    base.Initialize(requestContext);
    }
     

    这样,每次加载的时候都会根据相应的方法去实例IUserBusiness。

    在UserController中新建Post的LogOn方法

    代码

     
            [HttpPost]
    public ActionResult LogOn(User user, string returnUrl)
    {
    if (ModelState.IsValid)
    {
    if (UserBusiness.GetByNameAndPassword(user.Name, user.Password)!=null)
    {
    UserBusiness.SignIn(user.Name, user.RememberMe);

    if (Url.IsLocalUrl(returnUrl))
    {
    return Redirect(returnUrl);
    }
    else
    {
    return RedirectToAction("Index", "Home");
    }
    }
    else
    {
    ModelState.AddModelError(
    "", "提供的用户名或密码不正确。");
    }
    }

    // 如果我们进行到这一步时某个地方出错,则重新显示表单
    return View(user);
    }
     

    效果

    点击登录

    登陆后

    注销用户

    在UserController中添加LogOff的注销方法

     
    // **************************************
    // URL: /User/LogOff
    // **************************************

    public ActionResult LogOff()
    {
    UserBusiness.SignOut();

    return RedirectToAction("Index", "Home");
    }
     

    同样调用了 业务逻辑里面的注销方法。

    权限判断

    在之前我们加入了Roles也在业务逻辑里面添加了获取Role的方法,那么我们在什么时候来取得该用户的Role呢?

    我觉得是在页面验证之后要授权时,该方法在全局的Global.asax中,我们需要委托该事件。

    委托时我们再去根据当前用户获取角色,或者此时早已经将角色获取好了,只是加入到系统的Context之用。

    代码

     
    public MvcApplication()
    {
    AuthorizeRequest
    +=new EventHandler(MvcApplication_AuthorizeRequest);
    }

    void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
    {
    IIdentity id
    = Context.User.Identity;
    if (id.IsAuthenticated)
    {
    var roles
    =new UserBusiness().GetRoles(id.Name);
    string[] rolelist =newstring[] { roles.Name };
    Context.User
    =new GenericPrincipal(id, rolelist);
    }
    }
     

    然后我们在UserController只用需要权限的方法上加上对应的[Authorize]标志即可
    如代码

     
    //新建
    // GET: /User/Create

    [Authorize]
    public ActionResult Create()
    {
    return View();
    }
     

    如果你觉得,这样只是对于登录后所有的人有效,而你需要对指定的角色有效,你可以尝试用下面的方法。

    代码如下

     
    //新建
    // GET: /User/Create

    [Authorize(Roles
    ="admin")]
    public ActionResult Create()
    {
    return View();
    }
     

    此时如果你的用户不是admin那么你就请求不了该Create方法

    点击之后

    同样你可以重写该错误方法,让他弹出错误页面或者什么,那就看个人喜好了。

    总结

    其实像这种方法不仅仅是MVC特有的,在很多框架或者程序里面都用了类似的方法,总之还是借用了微软本身的这种窗体身份验证的特性。上面的方法只是一种思想,不一定会在程序中用到,现在很多老鸟都已经摆脱这种老套的身份验证了,对于新手菜鸟们拿来学习一下我觉得还是无可厚非的。

    转:记忆逝去的青春 http://www.cnblogs.com/lukun/ 

  • 相关阅读:
    IE浏览器中js使用中文标识符的bug
    Javascript变量作用域
    利用JS的动态语言特性对数组排序
    Javascript动态方法调用与参数修改的问题
    数组的平衡点
    Javascript中各种trim的实现
    js對象的比較
    返回两个数组中非相同的元素
    Javascript中匿名函数的多种调用方式
    SQL Server PreLogin Handshake Acknowledgement Error [duplicate]
  • 原文地址:https://www.cnblogs.com/jcomet/p/3128066.html
Copyright © 2020-2023  润新知