三層架構
前段時間公司要求修改一個網站,打開后我瘋了,一層沒有都是調用的DB接口,遍地的SQL語句,非常雜亂。
什么是三層架構?
三層架構是將整個項目划分為三個層次:表現層、業務邏輯層、數據訪問層。目的為了高內聚低耦合思想。
三層結構
表現層(UI):接受用戶請求,數據的返回呈現。
業務邏輯層(BLL ):用來處理業務邏輯,處理用戶提交的數據。
數據訪問層(DAL):用來與數據庫進行交互,處理增、刪、改、差。
實體類對象(Model):用來存儲實體類。
三層關系
UI表現層:接收用戶輸入的數據,並傳遞給業務邏輯層。
BLL業務邏輯層:把接收到的數據傳遞給數據訪問層,並返回結果給UI層。
DAL數據訪問層:負責與數據庫進行交互,並將結果返回給BLL層。
什么時候需要分層?
當項目過大的時候可以分層開發,這樣可以每個人負責的不同,共同進行開發。如果一個項目非常小的話,獨立開發可以不分層。
操作步驟:
1、創建結構
新增三個類庫分別是:BLL、DAL、Model,並建立一個UI層(winform 或 webform 或 MVC項目)設置啟動項為UI層。
這里隨個人喜歡可以再加一個Common層,負責處理其他常用方法。
2、引用關系
DAL要用到Model,BLL要用到DAL、CL和Model,UI要用到BLL、Model和CL。相互引用即可。
實現登陸
登陸是所有系統的入口,相信大家一般都用session,本例使用FormsAuthentication微軟提供的身份認證,存儲在cookie中。
假設有如下管理員表:
mif_id:唯一標識,lever等級,usernmae,psw賬號密碼,trueName姓名,createTime創建日期,isLock是否鎖定,Power權限。
編寫實體類

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Model 7 { 8 /// <summary> 9 /// 管理員實體類 10 /// </summary> 11 public class ManagerInfo 12 { 13 /// <summary> 14 /// 標識ID 15 /// </summary> 16 public int mif_id { get; set; } 17 /// <summary> 18 /// 暫時沒用 19 /// </summary> 20 public int mif_pid { get; set; } 21 /// <summary> 22 /// 管理員等級 23 /// </summary> 24 public int mif_lever { get; set; } 25 /// <summary> 26 /// 賬號 27 /// </summary> 28 public string mif_userName { get; set; } 29 /// <summary> 30 /// 密碼 31 /// </summary> 32 public string mif_psw { get; set; } 33 /// <summary> 34 /// 姓名 35 /// </summary> 36 public string mif_trueName { get; set; } 37 /// <summary> 38 /// 創建時間 39 /// </summary> 40 public DateTime mif_createTime { get; set; } 41 /// <summary> 42 /// 是否鎖定 43 /// </summary> 44 public bool mif_isLock { get; set; } 45 /// <summary> 46 /// 權限 47 /// </summary> 48 public string mif_power { get; set; } 49 } 50 }
習慣性編寫完Model重新生成下Model層,檢查是否有錯誤。
編寫數據訪問層
引入sqlHelper文件(上一文章中找),修改UI中的web.config添加數據庫連接字符串,並在sqlHelper中修改相關名稱,注意引入System.configuration.dll。
編寫操作ManagerInfo的數據訪問類

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Model; 6 using System.Data.SqlClient; 7 8 namespace DAL 9 { 10 /// <summary> 11 /// 管理員數據訪問類 12 /// </summary> 13 public class ManagerInfoDal 14 { 15 /// <summary> 16 /// 通過賬號查找 17 /// </summary> 18 /// <param name="userName"></param> 19 /// <returns></returns> 20 public static ManagerInfo row(string userName) 21 { 22 string sql = "select mif_id,mif_lever,mif_userName,mif_psw,mif_trueName,mif_createTime,mif_isLock from managerInfo where mif_userName = @uid"; 23 var v = new ManagerInfo(); 24 using (var dr = SQLHelper.ExecuteReader(sql, new SqlParameter("@uid", userName))) 25 { 26 if (dr.Read()) 27 { 28 v.mif_id = Convert.ToInt32(dr["mif_id"]); 29 v.mif_lever = Convert.ToInt32(dr["mif_lever"]); 30 v.mif_userName = dr["mif_userName"].ToString(); 31 v.mif_psw = dr["mif_psw"].ToString(); 32 v.mif_trueName = dr["mif_trueName"].ToString(); 33 v.mif_createTime = Convert.ToDateTime(dr["mif_createTime"]); 34 v.mif_isLock = Convert.ToBoolean(dr["mif_isLock"]); 35 } 36 } 37 return v; 38 } 39 } 40 }
編寫業務邏輯層

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using DAL; 6 using System.Web.Security; 7 using System.Web; 8 using CL; 9 using Model; 10 11 namespace BLL 12 { 13 /// <summary> 14 /// 管理員 15 /// </summary> 16 public class ManagerInfoBll 17 { 18 /// <summary> 19 /// 登陸 20 /// </summary> 21 /// <param name="userName"></param> 22 /// <param name="passWord"></param> 23 /// <param name="remember">是否記住</param> 24 /// <returns></returns> 25 public static string login(string userName, string passWord, string remember) 26 { 27 var v = ManagerInfoDal.row(userName); 28 if (v.mif_id > 0) 29 { 30 if (v.mif_isLock == false) 31 { 32 if (v.mif_psw.Equals(createMD5.getMD5(passWord))) 33 { 34 var expires = DateTime.Now.AddMinutes(20); 35 if (remember.Equals("on")) 36 { 37 expires = DateTime.Now.AddDays(8); 38 } 39 //將登陸的用戶存儲在Ticket中 40 var ticket = new FormsAuthenticationTicket(0, userName, DateTime.Now, expires, true, userName); 41 //使用Encrypt方法加密Ticket,並存儲在cookie中 42 var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket)); 43 //防止瀏覽器攻擊竊取、偽造cookie信息 44 cookie.HttpOnly = true; 45 cookie.Expires = expires; 46 HttpContext.Current.Response.Cookies.Add(cookie); 47 return "0"; 48 } 49 return "1"; 50 } 51 return "2"; 52 } 53 return "3"; 54 } 55 public static ManagerInfo row(string userName) 56 { 57 return ManagerInfoDal.row(userName); 58 } 59 /// <summary> 60 /// 查詢權限(通過數字查詢名字) 61 /// </summary> 62 /// <param name="userRole">權限數字</param> 63 /// <returns></returns> 64 public static string getRole(int userRole) 65 { 66 switch (userRole) 67 { 68 case 0: 69 return "編輯"; 70 case 1: 71 return "管理員"; 72 case 2: 73 return "系統"; 74 default: 75 return "暫無"; 76 } 77 } 78 } 79 }
管理員等級這里建議寫成枚舉,本人較懶。
FormsAuthenticationTicket這個東西比傳統的session和cookie的好處就是 可以任意目錄設置他的訪問權限,如果沒有登陸直接跳出到登陸頁面。而不用每個頁面一開始都認證一番~
還有 cookie.HttpOnly 在使用cookie時這一項最好設置一下,否則可能會客戶端模擬cookie進行攻擊。
編寫通用層

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Security.Cryptography; 5 using System.Text; 6 7 namespace CL 8 { 9 public class createMD5 10 { 11 public static string getMD5(string str) 12 { 13 var md5 = MD5.Create(); 14 var buffer = Encoding.Default.GetBytes(str); 15 var mdbuffer = md5.ComputeHash(buffer); 16 StringBuilder result = new StringBuilder(); 17 for (int i = 0; i < mdbuffer.Length; i++) 18 { 19 result.Append(mdbuffer[i].ToString("x2")); 20 } 21 return result.ToString(); 22 } 23 } 24 }
生成MD5的代碼,可不用。

1 using System; 2 using System.Collections.Generic; 3 using System.Drawing; 4 using System.Drawing.Drawing2D; 5 using System.Drawing.Imaging; 6 using System.IO; 7 using System.Linq; 8 using System.Text; 9 using System.Web; 10 11 namespace CL 12 { 13 public static class createVlidate 14 { 15 /// <summary> 16 /// 字符 17 /// </summary> 18 /// <param name="len">幾位</param> 19 /// <returns></returns> 20 public static string validation(int cd) 21 { 22 var ran = new Random(); 23 int num, tem; 24 string rtuStr = ""; 25 for (int i = 0; i < cd; i++) 26 { 27 num = ran.Next(); 28 if (i % 2 == 1) 29 tem = num % 10 + '0'; //數字 30 else 31 tem = num % 26 + 'A'; //字母 32 rtuStr += Convert.ToChar(tem).ToString(); 33 } 34 //寫入cookie 35 HttpCookie cookie = new HttpCookie("check"); 36 cookie.Value = rtuStr.ToLower(); 37 HttpContext.Current.Response.Cookies.Add(cookie); 38 return rtuStr; 39 } 40 41 /// <summary> 42 /// 生成圖像 43 /// </summary> 44 /// <param name="check">字符</param> 45 public static byte[] drawImg(string check) 46 { 47 Bitmap img = new Bitmap(90, 34); 48 var ht = Graphics.FromImage(img); 49 ht.Clear(Color.White); 50 ht.DrawLine(new Pen(Color.SpringGreen), 1, 1, 90, 34); 51 Font font = new Font("微軟雅黑", 20, FontStyle.Bold); 52 var jianbian = new LinearGradientBrush(new Rectangle(0, 0, img.Width, img.Height), Color.Teal, Color.Snow, 2f, true); 53 ht.DrawString(check, font, jianbian, 0, 0); 54 ht.DrawRectangle(new Pen(Color.Aqua), 0, 0, img.Width - 1, img.Height - 1); 55 MemoryStream ms = new MemoryStream(); 56 img.Save(ms, ImageFormat.Jpeg); 57 ht.Dispose(); 58 img.Dispose(); 59 return ms.ToArray(); 60 } 61 } 62 }
生成驗證碼的,可自己寫。
編寫展現層
首先需要在web.config中設置authentication節點為Forms驗證模式,然后就可以在目錄中任意設置訪問級別了。
<authentication mode="Forms"> <forms loginUrl="~/manage/index.html" timeout="10080" defaultUrl="~/manage/Net/" /> </authentication>
登陸頁面代碼

1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" /> 6 <meta http-equiv="X-UA-Compatible" content="IE=9" /> 7 <meta name="viewport" content="width=device-width, initial-scale=1"> 8 <title>漢之殤管理系統</title> 9 <link href="css/bootstrap.css" rel="stylesheet" /> 10 <link href="css/admin.css" rel="stylesheet" /> 11 <!--[if lt IE 9]> 12 <script src="http://apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script> 13 <script src="http://apps.bdimg.com/libs/respond.js/1.4.2/respond.min.js"></script> 14 <![endif]--> 15 </head> 16 <body class="login"> 17 <nav class="navbar navbar-default navbar-static-top"> 18 <div class="container"> 19 <div class="navbar-header"> 20 <a href="login.html" class="navbar-brand">漢之殤管理系統</a> 21 </div> 22 </div> 23 </nav> 24 <div class="container"> 25 <div class="panel panel-default"> 26 <div class="panel-body"> 27 <div id="ts"></div> 28 <h4 class="page-header">登陸</h4> 29 <div class="form-group has-feedback"> 30 <label class="sr-only" for="userName">賬號</label> 31 <input type="text" id="userName" class="form-control" placeholder="賬號" maxlength="50" autofocus /> 32 <span class="glyphicon glyphicon-user form-control-feedback" aria-hidden="true"></span> 33 </div> 34 <div class="form-group has-feedback"> 35 <label class="sr-only" for="passWord">密碼</label> 36 <input type="password" id="passWord" class="form-control" maxlength="50" placeholder="密碼" /> 37 <span class="glyphicon glyphicon-lock form-control-feedback" aria-hidden="true"></span> 38 </div> 39 <div class="form-group has-feedback"> 40 <label class="sr-only" for="validateCode">驗證碼</label> 41 <input type="text" id="validateCode" class="form-control validateCode" placeholder="驗證碼" maxlength="4" /> 42 <img src="checkLogin/ValidateCode.ashx" id="img" onclick="changeCode()" class="validateImg"> 43 <a href="javascript:changeCode()">看不清,換一張</a> 44 </div> 45 <div class="form-group"> 46 <input type="checkbox" id="remember" checked="checked" /> <span class="form-control-static">記住我 </span> 47 <button id="submit" type="button" class="btn btn-primary col-xs-offset-4" style="width:40%">登錄</button> 48 </div> 49 </div> 50 </div> 51 </div> 52 <nav class="navbar navbar-default navbar-fixed-bottom"> 53 <div class="container"> 54 <div class="navbar-header"> 55 <p class="navbar-text">© 2015 漢之殤 版權所有</p> 56 </div> 57 </div> 58 </nav> 59 <script src="js/jquery-1.7.2.min.js"></script> 60 <script src="js/status.js"></script> 61 <script src="js/login.js"></script> 62 </body> 63 </html>
驗證碼

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using CL; 6 7 namespace UI.manage.checkLogin 8 { 9 /// <summary> 10 /// ValidateCode 的摘要說明 11 /// </summary> 12 public class ValidateCode : IHttpHandler 13 { 14 public void ProcessRequest(HttpContext context) 15 { 16 context.Response.ContentType = "image/jpeg"; 17 18 var check = createVlidate.validation(4); 19 byte[] buffer = createVlidate.drawImg(check); 20 21 context.Response.BinaryWrite(buffer); 22 } 23 24 public bool IsReusable 25 { 26 get 27 { 28 return false; 29 } 30 } 31 } 32 }
登陸用的JS

1 $(function () { 2 $.get("checkLogin/Validate.ashx?rand=" + Math.random(0, 1), function (data) { 3 if (data == "0") { 4 location.href="/manage/Net/" 5 } 6 }); 7 8 if (top.location != self.location) { 9 top.location = self.location; 10 } 11 12 var lj = window.location.toString(); 13 if (lj.lastIndexOf("?") != -1) { 14 status("info", "請先登錄!"); 15 $("#userName").focus(); 16 } 17 18 $(".login").height(document.documentElement.clientHeight); 19 document.onkeydown = function (e) { 20 var ev = document.all ? window.event : e; 21 if (ev.keyCode == 13) { 22 $("#submit").click(); 23 } 24 } 25 }); 26 $("#submit").click(function () { 27 //$("#submit").attr("disabled", "true"); 28 var userName = $("#userName").val(); 29 var passWord = $("#passWord").val(); 30 var validateCode = $("#validateCode").val(); 31 var remember = $("#remember:checked").val(); 32 if (userName != "") { 33 if (passWord != "") { 34 if (validateCode != "") { 35 if (validateCode.toLowerCase() == getCode()) { 36 $.post("checkLogin/Validate.ashx", { userName: userName, passWord: passWord, remember: remember }, function (data) { 37 changeCode(); 38 if (data == "0") { 39 location.href = "Net/"; 40 } else if (data == "1") { 41 status("no", "登陸失敗,密碼錯誤!"); 42 $("#passWord").focus(); 43 return false; 44 } else if (data == "2") { 45 status("no", "登陸失敗,賬號已被禁止登陸!"); 46 $("#userName").focus(); 47 return false; 48 } else { 49 status("no", "登陸失敗,賬號不存在!"); 50 $("#userName").focus(); 51 return false; 52 } 53 }); 54 return false; 55 } 56 status("no", "驗證碼不正確!"); 57 $("#validateCode").focus(); 58 return false; 59 } 60 status("info", "請輸入驗證碼!"); 61 $("#validateCode").focus(); 62 return false; 63 } 64 status("info", "請輸入您的密碼!"); 65 $("#passWord").focus(); 66 return false; 67 } 68 status("info", "請輸入您的賬號!"); 69 $("#userName").focus(); 70 return false; 71 }); 72 function changeCode() { 73 $("#img").attr("src", $("#img").attr("src") + "?"); 74 } 75 function getCode() { 76 var cookies = document.cookie.split(";"); 77 for (var i = 0; i < cookies.length; i++) { 78 var validate = cookies[i].split("="); 79 if (validate[0].replace(/(^\s*)|(\s*$)/g, "") == "check") { 80 return validate[1].replace(/(^\s*)|(\s*$)/g, ""); 81 } 82 } 83 }
驗證的一般處理程序

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace UI.manage.checkLogin 7 { 8 /// <summary> 9 /// Validate 的摘要說明 10 /// </summary> 11 public class Validate : IHttpHandler 12 { 13 14 public void ProcessRequest(HttpContext context) 15 { 16 var userName = context.Request["userName"]; 17 var passWord = context.Request["passWord"]; 18 var remember = context.Request["remember"] == null ? "" : context.Request["remember"]; 19 if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(passWord)) 20 { 21 var result = BLL.ManagerInfoBll.login(userName, passWord, remember); 22 context.Response.Write(result); 23 } 24 else 25 { 26 if (context.Request.IsAuthenticated) 27 { 28 context.Response.Write("0"); 29 } 30 } 31 } 32 33 public bool IsReusable 34 { 35 get 36 { 37 return false; 38 } 39 } 40 } 41 }
最后附上效果圖如下:
登陸后使用 Page.User.Identity.Name 獲取用戶標識。
如下:
獲取信息及退出登陸如下:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Security; 6 using System.Web.UI; 7 using System.Web.UI.WebControls; 8 using BLL; 9 using Model; 10 11 namespace UI.manage.Net 12 { 13 public partial class _default : System.Web.UI.Page 14 { 15 protected void Page_Load(object sender, EventArgs e) 16 { 17 if (!IsPostBack) 18 { 19 var userName = Page.User.Identity.Name; 20 int roleId = ManagerInfoBll.row(userName).mif_lever; 21 22 this.userId.InnerText = userName; 23 this.userRole.InnerText = ManagerInfoBll.getRole(roleId); 24 } 25 26 var logout = Request["logout"]; 27 if (!string.IsNullOrEmpty(logout)) 28 { 29 FormsAuthentication.SignOut(); 30 Response.Redirect("../index.html"); 31 } 32 } 33 } 34 }
最后 在禁止匿名訪問的目錄下 新增一個web.config 內容如下
<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </configuration>
這樣,當記住憑證后直接訪問登陸或者該目錄都可以直接跳轉,如果點擊退出或過期后,則自動跳出到登陸頁面中。至此大功告成~