權限管理系統系列之序言
權限管理系統系列之WCF通信
之前寫了兩篇關於權限管理系統的博客了,由於這段時間有事比較忙就暫停了,今天繼續編寫權限管理系統之登陸和升級模塊,登陸和升級也是每個系統之必須的模塊,對於一個Winform程序,在登陸之前必須要先進行程序的升級,所以先介紹升級模塊。
表結構如下:
插入數據表數據如下:
一般程序升級可能有好幾種,比如說有根本版本號比較升級、根據DLL生成時間比較等等,而我介紹的是根據版本號進行升級。我們需要在服務端將DLL文件的 版本寫入到數據表中,這樣供每個客戶端去比較版本號,就是每次打開客戶端之前進行檢測版本號,如果版本號相等則不升級,如果不相等則進行升級操作。
服務端實現邏輯如下:
1 /// <summary> 2 /// 更新客戶端程序集信息 3 /// </summary> 4 public static void UpdateAssembleInfo() 5 { 6 DbHelper dbhelper = new DbHelper(AppServer.dbName); 7 string sql = "select * from t_fw_assemble_list t"; 8 DataTable dt = dbhelper.Query(sql); 9 DirectoryInfo dirInfo = new DirectoryInfo(Application.StartupPath); 10 FileVersionInfo fvi = null; 11 string updateSql = "update t_fw_assemble_list set s_versionno= '{0}',t_timestamp=getdate() where s_type='{1}' and s_filename='{2}'"; 12 string fName = string.Empty; 13 GetAllFiles(dirInfo); 14 bool isAddLog = true; 15 StreamReader sr = null; 16 for (int i = 0; i < dt.Rows.Count; i++) 17 { 18 fName = dt.Rows[i]["S_FILENAME"].ToString(); 19 if (fileNameDic.ContainsKey(fName)) 20 { 21 if (dt.Rows[i]["L_UPDATE"].ToString() == "2") 22 { 23 sr = new StreamReader(Application.StartupPath + "\\" + fName); 24 string fileContext = sr.ReadToEnd(); 25 sr.Close(); 26 sr.Dispose(); 27 sql = string.Format(updateSql, Common.Util.MD5Encrypt.MD5EncryptDES(fileContext), dt.Rows[i]["S_TYPE"], fName); 28 dbhelper.ExecuteSql(sql); 29 isAddLog = false; 30 } 31 else 32 { 33 fvi = FileVersionInfo.GetVersionInfo(fileNameDic[fName]); 34 if (!dt.Rows[i]["S_VERSIONNO"].ToString().Equals(fvi.FileVersion)) 35 { 36 sql = string.Format(updateSql, fvi.FileVersion, dt.Rows[i]["S_TYPE"], fName); 37 dbhelper.ExecuteSql(sql); 38 isAddLog = false; 39 } 40 } 41 if (!isAddLog) 42 { 43 isAddLog = AddLog(); 44 } 45 } 46 } 47 }
以上代碼主要是寫入版本到數據庫裡,每次服務端啟動首先執行這段代碼。
服務端搞定我就來看看客戶端了,客戶端啟動時調用以下方法實現:
1 bool isDownLoad = false; 2 if (args != null) 3 { 4 if (args.Length > 0) 5 { 6 for (int i = 0; i < args.Length; i++) 7 { 8 if (args[i] == "Update") 9 { 10 isDownLoad = true; 11 break; 12 } 13 } 14 } 15 } 16 if (isDownLoad) 17 { 18 //啟動主界面 19 Application.Run(new LoginForm()); 20 } 21 else 22 { 23 //更新客戶端程序集 24 if (UpdateAssembleData.UpdateAssembleInfo() > 0) 25 { 26 DownLoadForm dlf = new DownLoadForm(); 27 dlf.fileNameList = UpdateAssembleData.fileNameList; 28 //啟動下載程序界面 29 Application.Run(dlf); 30 } 31 else 32 { 33 //啟動主界面 34 Application.Run(new LoginForm()); 35 } 36 }
更新時會彈出升級窗體,上面會顯示升級的DLL文件,以及文件升級的進度條,升級完成啟動新的程序,升級過程的核心代碼:
1 /// <summary> 2 /// 下載文件 3 /// </summary> 4 private void DownLoadFile() 5 { 6 if (fileNameList.Count > 0) 7 { 8 //CallService service = new CallService("GetFile"); 9 int countLen = fileNameList.Count; 10 this.pbarDownLoad.Position = this.pbarDownLoad.Properties.Minimum; 11 double step = this.pbarDownLoad.Properties.Maximum / countLen; 12 string fName = string.Empty; 13 string upPath = Application.StartupPath + "\\Update\\"; 14 if (!Directory.Exists(upPath)) 15 { 16 Directory.CreateDirectory(upPath); 17 } 18 19 string sql = string.Empty; 20 FileStream fs; 21 bool isStartUpdate = false; 22 List<string> list = new List<string>(); 23 List<string> getList = new List<string>(); 24 //int fLen = 0; 25 long pageNum = 0; 26 for (int i = 0; i < countLen; i++) 27 { 28 bool isFirstPBLen = true; 29 isStartUpdate = false; 30 fName = fileNameList[i]; 31 IAsyncResult iart = this.lblDownLoad.BeginInvoke(new SetLabelText(AsyncLabel), new object[] { "正在下載文件 " + fName }); 32 this.lblDownLoad.EndInvoke(iart); 33 pageNum = 1; 34 list.Clear(); 35 list.Add(fName); 36 list.Add(pageNum.ToString ()); 37 38 //創建服務器下載的文件 39 string tmpPath = upPath + fName; 40 tmpPath = tmpPath.Substring(0, tmpPath.LastIndexOf('\\')); 41 if (!Directory .Exists (tmpPath)) 42 { 43 Directory.CreateDirectory(tmpPath); 44 } 45 fs = new FileStream(upPath + fName, FileMode.Create, FileAccess.Write); 46 47 while (true) 48 { 49 Result result = FileData.DoGetFile(list); 50 if (!result.success)//DoGetFile 51 { 52 Comm.WriteLogAndShowMessageBox.Error(result.msg, "Client.Win.DownLoadForm.DownLoadFile()出錯:" + result.msg); 53 StartUpdateApp(isStartUpdate); 54 break; 55 } 56 else 57 { 58 getList = JSON.Json2Object<List<string>>(result.data); //service.GetResult<List<string>>(); 59 byte[] buffer = Convert.FromBase64String(getList[2]); 60 61 if (isFirstPBLen) 62 { 63 //初始化當前文件進度條 64 iart = this.pbarCurrDownLoad.BeginInvoke(new UpdateProcessBar(AsyncIni), new object[] { Convert.ToInt32 (getList[0]), this.pbarCurrDownLoad }); 65 this.pbarCurrDownLoad.EndInvoke(iart); 66 isFirstPBLen = false; 67 Thread.Sleep(100); 68 } 69 70 //接收服務器返回的二制數據 71 fs.Write(buffer, 0, buffer.Length); 72 pageNum ++; 73 list[1] = pageNum.ToString(); 74 AsyncProcessBar(this.pbarCurrDownLoad, 1); 75 if (buffer.Length < Convert.ToInt32(getList[1])) 76 { 77 break; 78 } 79 80 } 81 } 82 fs.Flush(); 83 fs.Close(); 84 //插入日志記錄到服務器 85 86 Tools.WriteOptLogToDb(Tools.OPType.Update, "", fName, "從服務器更新文件" + fName); 87 //刷新進度條 88 if (this.pbarDownLoad.Position < this.pbarDownLoad.Properties.Maximum) 89 { 90 if (i == countLen - 1) 91 { 92 AsyncProcessBar(this.pbarDownLoad, step + 1); 93 } 94 else 95 { 96 AsyncProcessBar(this.pbarDownLoad, step); 97 } 98 } 99 isStartUpdate = true; 100 } 101 StartUpdateApp(isStartUpdate); 102 } 103 }
啟動本地程序:
1 /// <summary> 2 /// 啟動更新程序並退出本程序 3 /// </summary> 4 private void StartUpdateApp(bool isStartUpdate) 5 { 6 if (isStartUpdate) 7 { 8 string filePath = Application.StartupPath + "\\Update\\Update.exe"; 9 if (File.Exists (filePath)) 10 { 11 File.Copy(filePath, Application.StartupPath + "\\Update.exe" , true); 12 File.Delete(filePath); 13 } 14 15 Process.Start(Application.StartupPath + "\\Update.exe"); 16 } 17 //Environment.Exit(0); 18 Application.Exit(); 19 Process.GetCurrentProcess().Kill(); 20 }
以上基本上可以實現對程序的升級了。O(∩_∩)O哈哈~
用戶表結構:
升級完成後就該啟動登陸模塊。登陸界面如下:
相對而言登陸界面就超級簡單了,用戶名和密碼兩個文本框,兩個按鈕一個是登陸和一個取消按鈕,最下面顯示版權。
1 #region 按鈕 2 //登錄 3 private void logButton_Click(object sender, EventArgs e) 4 { 5 if (locked) 6 { 7 #region 解鎖 8 try 9 { 10 if (pswTextBox.Text == context.Password) 11 { 12 this.Hide(); 13 context.ParentForm.Enabled = true; 14 context.ParentForm.Show(); 15 Form[] childrens = context.ParentForm.OwnedForms; 16 if (childrens != null) 17 { 18 foreach (Form item in childrens) 19 { 20 if (item.IsDisposed) 21 continue; 22 if (!item.IsMdiChild && !"系統已鎖定".Equals(item.Text)) 23 { 24 item.Show(); 25 } 26 } 27 } 28 context.ParentForm.Activate(); 29 this.DialogResult = DialogResult.OK; 30 } 31 else 32 { 33 Comm.MessageBox.Info("密碼錯誤!"); 34 pswTextBox.Text = ""; ; 35 pswTextBox.Focus(); 36 return; 37 } 38 } 39 catch (Exception exMsg) 40 { 41 WriteLogAndShowMessageBox.Error("解鎖出錯:" + exMsg.Message, "解鎖出錯:" + exMsg.ToString()); 42 pswTextBox.Text = ""; ; 43 pswTextBox.Focus(); 44 } 45 #endregion 解鎖 46 } 47 else 48 { 49 #region 登錄 50 try 51 { 52 if (string.IsNullOrWhiteSpace(nameTextBox.Text)) 53 { 54 Comm.MessageBox.Info("請輸入賬號"); 55 nameTextBox.Focus(); 56 return; 57 } 58 if (string.IsNullOrWhiteSpace(pswTextBox.Text)) 59 { 60 Comm.MessageBox.Info("請輸入密碼"); 61 pswTextBox.Focus(); 62 return; 63 } 64 string name = nameTextBox.Text; 65 string password = pswTextBox.Text; 66 string despsw = Encrypt.EncryptDES(password, Const.EncryptKey); 67 68 Result result = LoginData.Login(name, despsw); 69 if (!result.success) 70 { 71 Comm.MessageBox.Info(result.msg); 72 if (result.msg.Contains("賬號不存在")) 73 { 74 nameTextBox.Text = ""; 75 nameTextBox.Focus(); 76 } 77 else if (result.msg.Contains("密碼錯誤")) 78 { 79 pswTextBox.Text = ""; 80 pswTextBox.Focus(); 81 } 82 else 83 { 84 nameTextBox.Text = ""; 85 pswTextBox.Text = ""; 86 nameTextBox.Focus(); 87 } 88 return; 89 } 90 else 91 { 92 t_fw_user user = new t_fw_user(); 93 user.s_usercode = name; 94 user.s_password = password; 95 string resData = JSON.Json2Object<string>(result.data); 96 user.s_username = resData; 97 //user.Mode = int.Parse(resData[1]); 98 //user.UserCode2 = resData[2]; 99 this.Hide(); 100 MainForm mainform = new MainForm(user); 101 mainform.Show(); 102 mainform.Text = systemName; 103 nameTextBox.Text = ""; 104 pswTextBox.Text = ""; 105 Log.Info(name + " 用戶登錄成功!"); 106 Tools.WriteOptLogToDb(Tools.OPType.Login, "", "用戶:" + name + "登錄", "用戶登錄"); 107 } 108 } 109 catch (Exception exMsg) 110 { 111 WriteLogAndShowMessageBox.Error("登錄出錯:" + exMsg.Message, "登錄出錯:" + exMsg.ToString()); 112 nameTextBox.Text = ""; 113 pswTextBox.Text = ""; 114 nameTextBox.Focus(); 115 } 116 #endregion 登錄 117 } 118 }
以上即可實現用戶的登陸,密碼采用DES加密,DES加密和解密方法如下:
1 /// <summary> 2 /// Description of Encrypt. 3 /// </summary> 4 public static class Encrypt 5 { 6 //默認密鑰向量 7 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; 8 /// <summary> 9 /// DES加密字符串 10 /// </summary> 11 /// <param name="encryptString">待加密的字符串</param> 12 /// <param name="encryptKey">加密密鑰,要求為8位</param> 13 /// <returns>加密成功返回加密後的字符串,失敗返回源串</returns> 14 public static string EncryptDES(string encryptString, string encryptKey) 15 { 16 if (!string.IsNullOrEmpty(encryptString)) 17 { 18 try 19 { 20 byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); 21 byte[] rgbIV = Keys; 22 byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); 23 DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); 24 MemoryStream mStream = new MemoryStream(); 25 CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); 26 cStream.Write(inputByteArray, 0, inputByteArray.Length); 27 cStream.FlushFinalBlock(); 28 return Convert.ToBase64String(mStream.ToArray()); 29 } 30 catch 31 { 32 return encryptString; 33 } 34 } 35 else 36 { 37 return string.Empty; 38 } 39 } 40 41 /// <summary> 42 /// DES解密字符串 43 /// </summary> 44 /// <param name="decryptString">待解密的字符串</param> 45 /// <param name="decryptKey">解密密鑰,要求為8位,和加密密鑰相同</param> 46 /// <returns>解密成功返回解密後的字符串,失敗返源串</returns> 47 public static string DecryptDES(string decryptString, string decryptKey) 48 { 49 try 50 { 51 byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey); 52 byte[] rgbIV = Keys; 53 byte[] inputByteArray = Convert.FromBase64String(decryptString); 54 DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); 55 MemoryStream mStream = new MemoryStream(); 56 CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); 57 cStream.Write(inputByteArray, 0, inputByteArray.Length); 58 cStream.FlushFinalBlock(); 59 return Encoding.UTF8.GetString(mStream.ToArray()); 60 } 61 catch 62 { 63 return decryptString; 64 } 65 } 66 }
現在應該所有的程序密碼都進行了加密了吧!之前好像CSDN把所有的密碼都洩露出來了,好像那時還是明文的,這好像還是去年的事情吧!
以上簡單介紹了升級和登陸模塊,也許做好這塊的功能是一個項目起到至關重要的作用。