ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(9)之系统登录
前面我们已经做好了一个文章管理功能模块,接下来,我们回头来做登录窗口,登录不仅涉及到登录验证还涉及到登录日志还有缓存时长等。
对于缓存的相关设置,我们已经写好封装在Bobo.Utilities.dll程序集中,我们只需要引用就好,没有的,请到此处下载。
在实现之前,我们需要先在表现层创建一个Resource文件夹,用于存放我们公共的文件、图片及模板等,然后下载
IPScaner.zip,并加压到Resource文件夹下。
QQwry.dat 纯真IP数据库收集了包括中国电信、中国网通、长城宽带、网通宽带、聚友宽带等 ISP 的最新准确 IP 地址数据。目前该库不是最新的数据,大家可以到网上下载到该目录下,这在我们做登录日志时可用到。
一、我们在区域BackstageModule下创建Login的控制器和视图:
(1)LoginController.cs控制器
using Bobo.Utilities; using Bobo.Utilities.Membership; using IA.Business.SystemBusiness; using IA.Entity; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; namespace IA.WebApp.Areas.BackstageModule.Controllers { /// <summary> /// 后台登录控制器 /// </summary> public class LoginController : Controller { // // GET: /BackstageModule/Login/ public ActionResult Index() { return View(); } /// <summary> /// 登录 /// </summary> /// <param name="Account"></param> /// <param name="Password"></param> /// <param name="Code"></param> /// <returns></returns> public ActionResult SubmitLogin(string Account, string Password, string Code) { try { Code = Md5Helper.CreateMd5(Code.ToLower(), 16); if (Session["session_Userloginvcode"].ToString() != Code) { return Content(new JsonMessage { Success = false, Code = "-2", Message = "验证码不正确" }.ToString()); } if (string.IsNullOrEmpty(Account) || string.IsNullOrEmpty(Password)) { return Content(new JsonMessage { Success = false, Code = "-1", Message = "用户名或密码错误!" }.ToString()); } IPScanerHelper objScan = new IPScanerHelper(); string IPAddress = NetHelper.LANIP; objScan.IP = IPAddress; objScan.DataPath = Server.MapPath("~/Resource/IPScaner/QQWry.Dat"); string IPAddressName = objScan.IPLocation(); int IsOk = new Base_UserBll().UserLogin(Account, Password, IPAddressName, IPAddress); string strCode = "-1"; string Message = ""; string czMsg = ""; bool Success = false; switch (IsOk) { case 1: strCode = "1"; Message = "登录成功!"; czMsg = "登录成功!"; Success = true; break; case 2: strCode = "-1"; Message = "登录失败,用户名或密码错误!"; czMsg = "密码错误!"; Success = false; break; case 3: strCode = "-1"; Message = "登录失败,用户名或密码错误!"; czMsg = "账号不存在!"; Success = false; break; } Base_SysLogBll.Instance.WriteLog(Account, OperationType.Login, strCode, czMsg + "、IP所在城市:" + IPAddressName); return Content(new JsonMessage { Success = Success, Code = strCode, Message = Message }.ToString()); } catch (Exception ex) { return Content(new JsonMessage { Success = false, Code = "-1", Message = "操作失败:" + ex.Message }.ToString()); } } /// <summary> /// 验证码 /// </summary> /// <returns></returns> public ActionResult VerifyCode() { int codeW = 80; int codeH = 22; int fontSize = 16; string chkCode = string.Empty; //颜色列表,用于验证码、噪线、噪点 Color[] color = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.Brown, Color.DarkBlue }; //字体列表,用于验证码 string[] font = { "Times New Roman", "Verdana", "Arial", "Gungsuh", "Impact" }; //验证码的字符集,去掉了一些容易混淆的字符 char[] character = { '2', '3', '4', '5', '6', '8', '9', 'a', 'b', 'd', 'e', 'f', 'h', 'k', 'm', 'n', 'r', 'x', 'y', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' }; Random rnd = new Random(); //生成验证码字符串 for (int i = 0; i < 4; i++) { chkCode += character[rnd.Next(character.Length)]; } //写入Session、验证码加密 Session["session_Userloginvcode"] = Md5Helper.MD5(chkCode.ToLower(), 16); //创建画布 Bitmap bmp = new Bitmap(codeW, codeH); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.White); //画噪线 for (int i = 0; i < 1; i++) { int x1 = rnd.Next(codeW); int y1 = rnd.Next(codeH); int x2 = rnd.Next(codeW); int y2 = rnd.Next(codeH); Color clr = color[rnd.Next(color.Length)]; g.DrawLine(new Pen(clr), x1, y1, x2, y2); } //画验证码字符串 for (int i = 0; i < chkCode.Length; i++) { string fnt = font[rnd.Next(font.Length)]; Font ft = new Font(fnt, fontSize); Color clr = color[rnd.Next(color.Length)]; g.DrawString(chkCode[i].ToString(), ft, new SolidBrush(clr), (float)i * 18 + 2, (float)0); } //将验证码图片写入内存流,并将其以 "image/Png" 格式输出 MemoryStream ms = new MemoryStream(); try { bmp.Save(ms, ImageFormat.Png); return File(ms.ToArray(), @"image/Gif"); } catch (Exception) { return null; } finally { g.Dispose(); bmp.Dispose(); } } /// <summary> /// 退出当前登录,清空所有用户cookie /// </summary> /// <returns></returns> public ActionResult LoginOut() { /***************************************** * 采用双cookie登录,则需要释放双cookie * 标准cookie记录用户主要信息,附加cookie用于前台 * 附加cookie可记录自定义的任何信息 * ***************************************/ //清空当前登录用户信息 ManageProvider.Provider.EmptyCurrent(); ManageProvider.Provider.EmptyCurrent<Base_User>(); Session.Abandon(); //取消当前会话 Session.Clear(); //清除当前浏览器所以Session return Content("1"); } } }
(2)Index视图
@{ ViewBag.Title = "登录"; } <script src="~/Content/Scripts/jquery/jquery-1.8.2.min.js"></script> <script src="~/Content/Scripts/jquery.form.js"></script> <script src="~/Content/Scripts/boboui-jshelp.js"></script> <script src="~/Content/Scripts/validator/boboui-validator.js"></script> <script src="~/Content/Scripts/layer/layer.js"></script> <script src="~/Content/Scripts/jQuery.md5.js"></script> <link href="~/Content/Styles/base.css" rel="stylesheet" /> <link href="~/Content/Styles/main.css" rel="stylesheet" /> <body style="background-color: #4e95e5; " class="font-yahei"> <div class="mainBox"> <h1 style="text-align: center; font-weight: bold; font-size:24px; color: #fff; padding-bottom: 25px; letter-spacing: 2px;">枼落NO.1</h1> <div class="Dialog-Box BoxS" style="margin: 0 auto; max-width: 500px; max-height: 300px; background-color: #fff; border-radius: 4px; padding: 0;"> <div class="Dialog-Tit-com" style="position:relative">系统用户登录</div> <div class="login-Ico L"><img src="~/Content/Images/slice/users_03.png" width="78" height="76" /> </div> <div class="login-box L"> <form id="form1" action="/BackstageModule/Login/SubmitLogin" method="post" enctype="multipart/form-data" style="margin: 1px"> <table class="layer-table-form"> <tr> <td><span class="layer-form-tit">用户名:</span><input style="height:30px;border-radius:4px;" type="text" name="Account" class="layer-form-txt" id="Account" datacol="yes" err="用户名" checkexpession="NotNull" /></td> </tr> <tr> <td><span class="layer-form-tit">密 码:</span><input style="height:30px;border-radius:4px;" type="password" name="Password" class="layer-form-txt" id="Password" datacol="yes" err="密码" checkexpession="NotNull" /></td> </tr> <tr> <td> <span class="layer-form-tit">验证码:</span><input type="text" name="Code" style="width: 100px; height: 30px; border-radius: 4px; " class="layer-form-txt" id="Code" datacol="yes" err="验证码" checkexpession="NotNull" /> <img id="codeImg" src="/BackstageModule/Login/VerifyCode" data-url="/BackstageModule/Login/VerifyCode" alt="点击切换验证码" onclick="switchCode($(this))" /> </td> </tr> </table> </form> </div> <div class="Dialog-sub-com" style="position:relative"> <input type="button" value="登录" class="Dialog-btn subBtn" onclick="AcceptClick()"> <input type="button" value="取消" class="Dialog-btn extBtn" onclick="selfClose()"> </div> </div> </div> <div class="clear" style="position:fixed;bottom:10px;color:#fff;text-align:center;width:100%;">页落素材网 Copyright 2008-2018 All Rights Reserved</div> </body> <script type="text/javascript"> //提交表单 function AcceptClick() { if (!CheckDataValid('#form1', true)) { return false; } var postData = GetWebControls("#form1"); postData.Password = escape($.md5(postData.Password)); $.post("/BackstageModule/Login/SubmitLogin?Code=" + $("#Code").val(), postData, function (data) { layer.msg(data.Message, { icon: data.Code, time: 1000 }, function () { if (data.Code > 0) { selfClose(); location.href = "/BackstageModule/ArticleMange/Index"; } else if (data.Code == -2) { $("#Code").val("").focus(); switchCode($("#codeImg")); } }); }, "json"); } //回车键 document.onkeydown = function (e) { if (!e) e = window.event; //火狐中是 window.event if ((e.keyCode || e.which) == 13) { AcceptClick(); } } //自定义关闭窗口 function selfClose() { var index = parent.layer.getFrameIndex(window.name); parent.layer.close(index); } //切换验证码 function switchCode(obj) { var url = obj.attr("data-url"); url += "?date=" + new Date().toString(); obj.attr("src", url); } </script>
二、在完成登录操作的过程后,我们需要对登录权限的认证,也就是非登录不可访问。
首先我们需要在表现层中的SystemExtension文件夹中添加LoginAuthorizeAttribute.cs和ManagerPermissionAttribute.cs登录权限类:
(1)LoginAuthorizeAttribute.cs
using Bobo.Utilities; using Bobo.Utilities.Membership; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace IA.WebApp.SystemExtension { /// <summary> /// 登录权限认证 /// </summary> public class LoginAuthorizeAttribute : AuthorizeAttribute { //地址参数的绝对路径("~/aaa/vvv") private String _LoginUrl = String.Empty; //无参构造器 public LoginAuthorizeAttribute() { } /// <summary> /// 标记构造器,可传入登陆地址 /// </summary> /// <param name="LoginUrl">地址参数的绝对路径("~/aaa/vvv")</param> public LoginAuthorizeAttribute(string LoginUrl) { this._LoginUrl = LoginUrl; } /// <summary> /// 响应前执行验证,查看当前用户是否有效 /// </summary> /// <param name="filterContext"></param> public override void OnAuthorization(AuthorizationContext filterContext) { var areaName = filterContext.RouteData.DataTokens["area"]; var controllerName = filterContext.RouteData.Values["controller"]; var action = filterContext.RouteData.Values["Action"]; var returnUrl = filterContext.RequestContext.HttpContext.Request.Url.ToString(); if (!StringHelper.IsNullOrEmpty(this._LoginUrl) && this._LoginUrl.IndexOf('?') == -1) { this._LoginUrl += ("?ReturnUrl=" + StringHelper.Escape(returnUrl)); } if (this._LoginUrl.IndexOf("BackstageModule") != -1) { if (!ManageProvider.Provider.IsOverdue()) { filterContext.Result = new RedirectResult(this._LoginUrl); } else { if (ManageProvider.Provider.Current().Code != "System") { filterContext.Result = new RedirectResult(this._LoginUrl); } } } else { //登录是否过期 if (!ManageProvider.Provider.IsOverdue()) { filterContext.Result = new RedirectResult(this._LoginUrl); } else { if (ManageProvider.Provider.Current().Code == "System") { filterContext.Result = new RedirectResult(this._LoginUrl); } } } } } }
(2)ManagerPermissionAttribute.cs
using Bobo.Utilities; using Bobo.Utilities.Membership; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace IA.WebApp.SystemExtension { public class ManagerPermissionAttribute : AuthorizeAttribute { private PermissionMode _CustomMode; /// <summary>默认构造</summary> /// <param name="Mode">权限认证模式</param> public ManagerPermissionAttribute(PermissionMode Mode) { _CustomMode = Mode; } /// <summary>权限认证</summary> /// <param name="filterContext"></param> public override void OnAuthorization(AuthorizationContext filterContext) { //登录权限认证 if (!ManageProvider.Provider.IsOverdue()) { filterContext.Result = new RedirectResult("~/Login/Default"); } //防止被搜索引擎爬虫、网页采集器 if (!this.PreventCreeper()) { filterContext.Result = new RedirectResult("~/Login/Default"); } //权限拦截是否忽略 if (_CustomMode == PermissionMode.Ignore) { return; } //执行权限认证 if (!this.ActionAuthorize(filterContext)) { ContentResult Content = new ContentResult(); Content.Content = "<script type='text/javascript'>top.Loading(false);alert('很抱歉!您的权限不足,访问被拒绝!');</script>"; filterContext.Result = Content; } } /// <summary> /// 执行权限认证 /// </summary> /// <returns></returns> private bool ActionAuthorize(AuthorizationContext filterContext) { if (ManageProvider.Provider.Current().IsSystem && ManageProvider.Provider.Current().CompanyCategory != "SystemUnit") return true; var areaName = filterContext.RouteData.DataTokens["area"] + "/"; //获取当前区域 var controllerName = filterContext.RouteData.Values["controller"] + "/"; //获取控制器 var action = filterContext.RouteData.Values["Action"]; //获取当前Action string requestPath = "/" + areaName + controllerName + action; //拼接构造完整url string ModuleId = DESEncrypt.Decrypt(CookieHelper.GetCookie("ModuleId")); //bool Result = Base_ModulePermissionBll.Instance.ActionAuthorize(requestPath.ToLower(), ManageProvider.Provider.Current().ObjectId, ModuleId, ManageProvider.Provider.Current().UserId); return true;// Result; } /// <summary> /// CA验证方法 /// </summary> /// <param name="filterContext"></param> /// <returns></returns> private bool ActionCA(AuthorizationContext filterContext) { //TODO:如何在过滤器调用CA的前台检测方法返回值(NC) if (ManageProvider.Provider.Current().IsSystem && ManageProvider.Provider.Current().CompanyCategory != "SystemUnit") { return true; } else { return true; } } /// <summary> /// 防止被搜索引擎爬虫、网页采集器 /// </summary> /// <returns></returns> private bool PreventCreeper() { return true; } } }
权限认证做好后,我们在需要权限认证的控制器上加上[LoginAuthorize("~/BackstageModule/Login/Index")]这句了,例如文章控制器:
然后改造公共布局页_LayoutMange.cshtml:
@using Bobo.Utilities.Membership @using IA.Business <!DOCTYPE html> <html> @*后台公共模块*@ <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" media="screen" /> <link href="~/Content/Styles/main.css" rel="stylesheet" /> <link href="~/Content/Styles/base.css" rel="stylesheet" /> <script src="~/Content/Scripts/datepicker/WdatePicker.js"></script> <script src="~/Content/Scripts/jquery/jquery-1.8.2.min.js"></script> <script src="~/Content/Scripts/jquery.form.js"></script> <script src="~/Content/Scripts/boboui-jshelp.js"></script> <script src="~/Content/Scripts/validator/boboui-validator.js"></script> <script src="~/Content/Scripts/layer/layer.js"></script> <script src="~/Content/Scripts/Juicer/juicer.js"></script> <script src="~/Content/Scripts/m_pager.js"></script> </head> <body class="set_body"> <div class="w_header font-yahei"> <div class="header-item"> <div class="mAuto"> <div class="L">页落素材网 - 用户管理后台 【当前用户】:@ManageProvider.Provider.Current().UserName</div> <div class="R"><a href="javascript:;" id="LoginOut" style="color: #00b7f0;">【退出登录】</a></div> </div> </div> <div class="header-nav clear"> <div class="mAuto"> <a href="/" class="logo L"> <img src="~/Content/Images/slice/logoMange.png" /></a> <ul class="R nav-item"> <li class="L"><a href="/BackstageModule/Article/Index" class="wzgl">网站管理</a></li> <li class="L"><a href="/BackstageModule/TrainSignupInfo/Index" class="hypx">区块链</a></li> <li class="L"><a href="/BackstageModule/MemberManagement/Index" class="hygl">人工智能</a></li> <li class="L"><a href="/">返回首页</a></li> </ul> </div> </div> </div> @RenderBody() <div id="footer"> <div class="foot-tip w_footer"> <div class="webPage text-center"> 页落素材网是网页特效下载社区,以提高效率、分享经验的理念,提供高品质实用、简单、易懂的Web页面特效。 </div> </div> </div> <script type="text/javascript"> $(function () { $("#LoginOut").on("click", function () { AjaxJson("/BackstageModule/Login/LoginOut", {}, function (data) { layer.msg("您已退出登录,欢迎再来哦 ^_^", { icon: 6, shade: [0.3, '#000'] }, function () { location.href = "/BackstageModule/Login/Index"; }); }); }); }) //复选框选定事件 function change(obj) { var tr = obj.parentElement.parentElement; if (obj.checked) { tr.style.backgroundColor = '#e5eaff'; tr.style.borderColor = '#cacccb'; } else { tr.style.backgroundColor = '#fff'; } } </script> @RenderSection("scripts", required: false) </body> </html>
登录效果图预览