目录
权限管理系统系列之序言
权限管理系统系列之WCF通信
之前写了两篇关于权限管理系统的博客了,由于这段时间有事比较忙就暂停了,今天继续编写权限管理系统之登陆和升级模块,登陆和升级也是每个系统之必须的模块,对于一个Winform程序,在登陆之前必须要先进行程序的升级,所以先介绍升级模块。
升级模块
表结构如下:
插入数据表数据如下:
一般程序升级可能有好几种,比如说有根本版本号比较升级、根据DLL生成时间比较等等,而我介绍的是根据版本号进行升级。我们需要在服务端将DLL文件的 版本写入到数据表中,这样供每个客户端去比较版本号,就是每次打开客户端之前进行检测版本号,如果版本号相等则不升级,如果不相等则进行升级操作。
服务端实现逻辑如下:
1 ///
2 /// 更新客户端程序集信息 3 /// 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 ///
2 /// 下载文件 3 /// 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.Json2Objectstring>>(result.data); //service.GetResult>(); 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 ///
2 /// 启动更新程序并退出本程序 3 /// 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 ///2 /// Description of Encrypt. 3 /// 4 public static class Encrypt 5 { 6 //默认密钥向量 7 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; 8 /// 9 /// DES加密字符串 10 /// 11 /// 待加密的字符串 12 /// 加密密钥,要求为8位 13 /// 加密成功返回加密后的字符串,失败返回源串 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 /// 42 /// DES解密字符串 43 /// 44 /// 待解密的字符串 45 /// 解密密钥,要求为8位,和加密密钥相同 46 /// 解密成功返回解密后的字符串,失败返源串 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把所有的密码都泄露出来了,好像那时还是明文的,这好像还是去年的事情吧!
以上简单介绍了升级和登陆模块,也许做好这块的功能是一个项目起到至关重要的作用。