·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> 分享一个html+js+ashx+easyui+ado.net权限管理系统
EasyUI、权限管理 这是个都快被搞烂了的组合,但是easyui的确好用,权限管理在项目中的确实用。一直以来博客园里也不少朋友分享过,但是感觉好的要不没源码,要不就是过度设计写的太复杂看不懂,也懒得去看懂,还有一些不是在推广自己的代码生成器就是在卖权限组件,看着漂亮的UI和完善的功能就是没源码学习,真是恼人。
前段时间公司项目阶段性结束了,就抽空把权限控制的部分抽取出来写了个html+js+ashx+ado.net的权限管理系统分享给一些初学者,这个权限系统demo没有MVC、没有ORM、数据库表都没设外键关系、级联删除等,所有需要级联操作的地方都是事务提交。界面上的所有操作基本都是jquery发Ajax请求ashx处理,ashx处理后输出json前台接收处理并配合easyui的组件响应给用户。基本没什么门槛,比较适合初学者。先看项目结构图:
项目结构基本就是模仿PetShop的,简单的7层,接口里定义方法,不同数据库不同实现,工厂负责创建访问数据库的对象,具体访问哪个数据库写在配置文件,都是老东西了也没什么说的。Model、BLL、SQLServerDAL等类库里的类都跟数据库表名保持一致,新建类库的时候修改了默认的命名空间(右键类库 - 属性 - 应用程序 - 程序集名称&默认命名空间),调用的时候用解决方案名.类库名.类名,这是我的个人习惯。具体查看源码
接下来简单分享下代码和贴图演示,懒的听我啰嗦的直接跳转到文章结尾下载源码。戳我
登陆就是用框架提供的FormsAuthentication类来做的,基本就是写cookie了,用户登录成功就加密下票证写到cookie里,简单的SetAuthCookie方法有点太简单了,只能写用户名到cookie里。我一般用FormsAuthenticationTicket类来做,可以把整个用户对象(userData)都写到cookie里。如果只把用户名写到cookie里,这样用户在别的浏览器登录然后执行修改密码操作,过来之前登陆过的浏览器,虽然改了密码,但还是可以继续保持登陆状态(博客园就是),这显然不符合常理。我的是把用户名和密码都保存到cookie里,然后用户每次访问都取出用户名和密码去数据库验证,如果找不到记录就干掉cookie。说到这肯定有人疑惑,把用户密码写到cookie里会不会不安全,用户密码本身就是不可逆的md5,写入cookie之前也再次进行了加密,我个人相信是比较安全的,且只有你自己看到cookie,如果担心有人抓包,可以把登陆功能部署到https上(个人想法,欢迎拍砖)。
简单来说,我的登陆逻辑:用户访问登陆页面就ajax请求后台验证cookie,只有用户名和密码匹配上(用户没修改密码)、状态IsAble可用(管理员没在后台禁用此用户)等等的情况下直接跳到首页,其他都干掉cookie。这样做的好处就是管理员可以很方便的控制一个用户的状态,就算他保存了cookie,因为服务端每次都有验证IsAble字段,管理员也可以很方便的禁用这个用户。还有不影响登陆的情况:比如用户修改了自己的姓名等情况也得重写cookie,否则从cookie里取出来的用户名显示到欢迎区域就不准确了,这里用FormsAuthenticationTicket就完美了,userData参数可以存很多东西。
首次访问登陆页面判断是否登陆和用户点击登陆按钮的示例代码:
case "iflogin": //System.Threading.Thread.Sleep(5000); if (context.Request.IsAuthenticated) { FormsIdentity id = (FormsIdentity)context.User.Identity; FormsAuthenticationTicket tickets = id.Ticket; //获取票证里序列化的用户对象(反序列化) ZGZY.Model.User userCheck = new javaScriptSerializer().Deserialize<ZGZY.Model.User>(tickets.UserData); //执行登录操作 ZGZY.Model.User userReLogin = new ZGZY.BLL.User().UserLogin(userCheck.UserId, userCheck.UserPwd); if (userReLogin == null) { FormsAuthentication.SignOut(); context.Response.Write("{\"msg\":\"用户名或密码错误!\",\"success\":false}"); } else if (!userReLogin.IsAble) { FormsAuthentication.SignOut(); context.Response.Write("{\"msg\":\"用户已被禁用!\",\"success\":false}"); } else { //记录登录日志 ZGZY.Model.LoginLog loginInfo = new Model.LoginLog(); loginInfo.UserIp = context.Request.UserHostAddress; loginInfo.City = context.Request.Params["city"] ?? "未知"; //访问者所处城市 loginInfo.UserName = context.User.Identity.Name; loginInfo.Success = true; new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo); context.Response.Write("{\"msg\":\"已登录过,正在跳转!\",\"success\":true}"); } } else context.Response.Write("{\"msg\":\"nocookie\",\"success\":false}"); break;case "login": //System.Threading.Thread.Sleep(5000); string userIp = context.Request.UserHostAddress; string city = context.Request.Params["city"] ?? "未知"; string remember = context.Request.Params["remember"] ?? ""; //记住密码天数 string name = context.Request.Params["loginName"]; string pwd = ZGZY.Common.Md5.GetMD5String(context.Request.Params["loginPwd"]); //md5加密 DateTime? lastLoginTime; if (new ZGZY.BLL.LoginLog().CheckLogin(userIp, out lastLoginTime) != null) { DateTime dtNextLogin = Convert.ToDateTime(lastLoginTime); context.Response.Write("{\"msg\":\"密码错误次数达到5次,请在" + dtNextLogin.AddMinutes(30).ToShortTimeString() + "之后再登陆!\",\"success\":false}"); } else { ZGZY.Model.LoginLog loginInfo = new Model.LoginLog(); loginInfo.UserName = name; loginInfo.UserIp = userIp; loginInfo.City = city; ZGZY.Model.User currentUser = new ZGZY.BLL.User().UserLogin(name, pwd); if (currentUser == null) { context.Response.Write("{\"msg\":\"用户名或密码错误!\",\"success\":false}"); loginInfo.Success = false; new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo); } else if (currentUser.IsAble == false) { context.Response.Write("{\"msg\":\"用户已被禁用!\",\"success\":false}"); loginInfo.Success = false; new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo); } else { //记录登录日志 loginInfo.Success = true; new ZGZY.BLL.LoginLog().WriteLoginLog(loginInfo); context.Response.Write("{\"msg\":\"登录成功!\",\"success\":true}"); DateTime dateCookieExpires; //cookie有效期 switch (remember) { case "notremember": dateCookieExpires = new DateTime(9999, 12, 31); //默认时间 break; case "oneday": dateCookieExpires = DateTime.Now.AddDays(1); break; case "sevenday": dateCookieExpires = DateTime.Now.AddDays(7); break; case "onemouth": dateCookieExpires = DateTime.Now.AddDays(30); break; case "oneyear": dateCookieExpires = DateTime.Now.AddDays(365); break; default: dateCookieExpires = new DateTime(9999, 12, 31); break; } FormsAuthenticationTicket ticket = new FormsAuthenticationTicket ( 2, currentUser.UserId, DateTime.Now, dateCookieExpires, false, new JavascriptSerializer().Serialize(currentUser) //序列化当前用户对象 ); string encTicket = FormsAuthentication.Encrypt(ticket); HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); if (dateCookieExpires != new DateTime(9999, 12, 31)) //不是默认时间才设置过期时间,否则会话cookie cookie.Expires = dateCookieExpires; context.Response.Cookies.Add(cookie); } } break;
博客园的做法是访问登陆页面就把cookie干掉(如果有),我个人还是觉得有cookie再访问登陆页面就跳转到首页比较好。你可以访问博客园的登陆页面试试(慎点,会干掉你的cookie)
另外登陆功能还调用了sina的api获取用户登录城市、同一个ip连续5次输错密码就30分钟之内不让登陆,详细请自己查看源码。
权限控制基本就是用户拥有角色(可以多角色)、角色拥有菜单不同按钮的权限(浏览、增加、修改、删除等)。这样基本做到了单用户多角色,界面上的操作按钮根据用户拥有的权限显示或者不显示。先添加一个用户,默认密码123:
"已经改密"如果不勾选上,那么下次这个用户登录就会弹框让他修改密码(这个功能是跟添加用户默认密码是123相互呼应的)。直接用添加的用户登录会什么都没有,因为此用户没有任何菜单权限:
左侧的目录树是EasyUI的Tree组件,打开页面的时候ajax取出当前用户拥有的菜单权限然后展示出来,不同用户看到的菜单是不一样的。后台操作基本就是一个连表查询,DataTable取出来然后遍历构建这个Tree:
/// <summary>/// 根据用户主键id查询用户可以访问的菜单/// </summary>public DataTable GetUserMenu(int id){ StringBuilder strSql = new StringBuilder(); strSql.Append("select distinct(m.Name) menuname,m.Id menuid,m.Icon icon,u.Id userid,u.UserId username,m.ParentId menuparentid,m.Sort menusort,m.LinkAddress linkaddress from tbUser u"); strSql.Append(" join tbUserRole ur on u.Id=ur.UserId"); strSql.Append(" join tbRoleMenuButton rmb on ur.RoleId=rmb.RoleId"); strSql.Append(" join tbMenu m on rmb.MenuId=m.Id"); strSql.Append(" where u.Id=@Id order by m.ParentId,m.Sort"); return ZGZY.Common.SqlHelper.GetDataTable(ZGZY.Common.SqlHelper.connStr, CommandType.Text, strSql.ToString(), new SqlParameter("@Id", id));}
重新登陆下管理员账户添加一个浏览角色:
给角色授权: