權限管理系統系列之序言
權限管理系統系列之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把所有的密碼都洩露出來了,好像那時還是明文的,這好像還是去年的事情吧!
以上簡單介紹了升級和登陸模塊,也許做好這塊的功能是一個項目起到至關重要的作用。