class="postTitle">权限管理系统系列之序言
权限管理系统系列之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把所有的密码都泄露出来了,好像那时还是明文的,这好像还是去年的事情吧!
以上简单介绍了升级和登陆模块,也许做好这块的功能是一个项目起到至关重要的作用。