博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我要学ASP.NET MVC 3.0(十): MVC 3.0 使用 Forms身份验证
阅读量:2386 次
发布时间:2019-05-10

本文共 8279 字,大约阅读时间需要 27 分钟。

概述

 

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

页面控件引用

 

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

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

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

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

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

代码

@Html.Partial("_LogOnPartial")

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

 

实现登录

 

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

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

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

创建用户Model类

public class User    {        public int ID { get; set; }        [DisplayName("姓名")]        [Required(ErrorMessage = "姓名不能为空")]        public string Name { get; set; }        [DisplayName("密码")]        [Required]        [DataType(DataType.Password)]        public string Password { get; set; }        [Display(Name = "记住我?")]        public bool RememberMe { get; set; }        public Roles Roles { get; set; }           }

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

代码:

public class Roles    {        public int ID { get; set; }        public string 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";}

用户登录

@using (Html.BeginForm()){ @Html.ValidationSummary(true)
登录信息
@Html.LabelFor(model => model.Name)
@Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name)
@Html.LabelFor(model => model.Password)
@Html.EditorFor(model => model.Password) @Html.ValidationMessageFor(model => model.Password)
@Html.LabelFor(model => model.RememberMe)
@Html.EditorFor(model => model.RememberMe) @Html.ValidationMessageFor(model => model.RememberMe)

}
@Html.ActionLink("返回主页", "Index")

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

业务逻辑接口

创建一个名为IUserBusiness接口

public interface IUserBusiness    {        ///         /// 获取用户角色        ///         ///         /// 
Roles GetRoles(string userName); /// /// 根据用户名和密码获取用户信息 /// /// /// ///
User GetByNameAndPassword(string name, string password); /// /// 登录 /// /// /// void SignIn(string userName, bool createPersistentCookie); /// /// 注销 /// void SignOut(); }

实现接口

public class UserBusiness : IUserBusiness    {        ///         /// 作为模拟的数据集        ///         private static 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"}}        };        ///         /// 根据用户名获取角色        ///         ///         /// 
public Roles GetRoles(string userName) { return UserList .Where(o=> o.Name == userName) .Select(o => o.Roles) .FirstOrDefault(); } /// /// 根据用户名和密码获取用户 /// /// /// ///
public User GetByNameAndPassword(string name, string password) { return UserList .FirstOrDefault(u => u.Name == name && u.Password == password); } /// /// 登录 /// /// /// public void SignIn(string userName, bool createPersistentCookie) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("值不能为 null 或为空。", "userName"); FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); } /// /// 注销 /// public void 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; }        protected override void 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 = new string[] { 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://nmjab.baihongyu.com/

你可能感兴趣的文章
collective intelligence framework
查看>>
2015年关注的技术书籍
查看>>
windows 2003 server 记录远程桌面的连接登录日志和修改3389连接端口方法
查看>>
samhain:比较变态的入侵检测系统
查看>>
Linux psacct文档
查看>>
使用setuptools自动安装python模块
查看>>
python IDE环境
查看>>
传说中的windows加固 -.... -
查看>>
windows目录监控软件
查看>>
Virus Bulletin malware分析杂志以及paper
查看>>
Security Considerations for AppLocker
查看>>
Oracle Forensics t00ls
查看>>
JetLeak Vulnerability: Remote Leakage Of Shared Buffers In Jetty Web Server [CVE-2015-2080]
查看>>
windows event logs分析
查看>>
python网络编程
查看>>
Linux Hardening & Security
查看>>
免费而强大的linux防火墙:APF
查看>>
DNS安全相关
查看>>
FakeIKEd
查看>>
LFI fuzzer
查看>>