接前文系列四,本文将讲解实现工资管理系统的代码的层次结构。主要采用的是MVCS模式的代码层次结构,视图层(V):是各种winform窗体;控制层(C):主要是winform窗体中各种控件的各个逻辑处理事件;而模型层(M)则是各个model实体以及数据库访问层的代码;最后是服务层(S):是从模型层(M)中获取数据库访问操作的结果并为逻辑控制处理层(C)提供服务的层次。除此之外,还有另外的几个层次:工具类层util:主要是本系统共用的一些工具类的操作;报表打印层report:主要是在处理报表方面的共有操作;公共信息层commonMessage:主要是存放本系统在进行相关逻辑处理时候需要存放的一些通用的信息;最后是存放“图片或者文件”的文件层imageAndFile。如下图所示
在此说明一下,本系统其实并非传统的桌面C/S的单机版办公系统,经过测试,本系统是可以在局域网内实现服务器共享的C/S的桌面办公系统,只需要对服务器进行相应的配置,从而实现只需在每台主机安装一个本系统的客户端,就可以访问局域网内的服务器的数据了!
下面介绍一下本管理系统登录模块的实现。在前期,本管理系统的登录比较简单,只是采用userName和password明文进行登录验证而已!今天心血来潮,觉得“一个好的管理系统应该有登录功能,而登录则会涉及密码的验证,而密码的使用必然涉及到安全性问题,而安全则会不自觉跟加密解密挂上钩!”,所以,今天花了一个多小时,实现了采用MD5+自定义的字符串 实现用户登录与注册时候的密码解密与密码加密的功能,在下面会进行介绍!
下图是本系统用户登录的流程图。在贴上用户登录代码之前,需要先将获取数据库连接、数据库访问层的代码和系统共用的类的代码贴上!在系统后续的讲解中会时常使用到,但由于篇幅原因,并没有贴上全部,所以如果需要的话,可以QQ联系我QQ:1974544863(记得备注:桌面办公应用),我愿意低出售你并与你进行相关交流!
后续系统中出现SqlConnection conn = DBConnection.MyConnection(); 是从dbConnection包中获取的,主要的代码为:
using System; using System.Collections.Generic; using System.Text; using System.Data.SqlClient;//引用SQL命名空间 using System.Windows.Forms; namespace SMS.dbConnection { class DBConnection//定义类型 { /// <summary> /// 返回数据库连接的静态方法 /// </summary> /// <returns>方法返回数据库连接对象</returns> public static SqlConnection MyConnection() { return new SqlConnection(//创建数据库连接对象 @"server=.;database=db_SMS;uid=sa;pwd=123456");//数据库连接字符串 } } }
而operate和cmmUtils是在类的开始之处定义的:
public frmLogin() { InitializeComponent(); } //创建数据库操作对象 DBOperate operate = new DBOperate(); //工具类 CommonUtils cmmUtils = new CommonUtils();
而DBOperate类和CommonUtils类的代码的作用分别为:数据库访问和共用的工具类,下面贴出各自的部分代码,下面是DBOperate类的代码:
using System; using System.Collections.Generic; using System.Text; using System.Data.SqlClient; using System.Windows.Forms; using System.Data; using System.IO; using System.Drawing; using SMS.dbConnection; namespace SMS.dbOperation { class DBOperate { //获取数据库链接 SqlConnection conn = DBConnection.MyConnection(); /// <summary> /// 操作数据库,执行各种SQL语句 /// </summary> /// <param name="strSql">SQL语句</param> /// <returns>方法返回受影响的行数</returns> public int OperateData(String strSql) { try { conn.Open();//打开数据库连接 SqlCommand cmd = new SqlCommand(strSql, conn);//创建命令对象 int i = (int)cmd.ExecuteNonQuery();//执行SQL命令 conn.Close();//关闭数据库连接 return i;//返回数值 } catch (System.Exception ex) { return -1; } } /// <summary> /// 根据SQL语句查询返回n行数据 /// </summary> /// <param name="sql"></param> /// <param name="conn"></param> /// <returns></returns> public SqlDataReader getSQLData(String sql,SqlConnection conn) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); SqlDataReader sdr = cmd.ExecuteReader(); return sdr; } /// <summary> /// 方法用于绑定DataGridView控件 /// </summary> /// <param name="dgv">DataGridView控件</param> /// <param name="sql">SQL语句</param> public void BindDataGridView(DataGridView dgv, String sql) { SqlDataAdapter sda = new SqlDataAdapter(sql, conn);//创建数据适配器对象 DataSet ds = new DataSet();//创建数据集对象 sda.Fill(ds);//填充数据集 dgv.DataSource = ds.Tables[0];//绑定到数据表 ds.Dispose();//释放资源 } /// <summary> /// 查找某个字段数量 /// </summary> /// <param name="strsql">SQL语句</param> /// <returns>方法返回指定记录的数量</returns> public int HumanNum(String strsql) { conn.Open();//打开数据库连接 SqlCommand cmd = new SqlCommand(strsql, conn);//创建命令对象 int i = (int)cmd.ExecuteScalar();//执行SQL命令 conn.Close();//关闭数据库连接 return i;//返回数值 } /// <summary> /// 根据数据库字段组成的sql语句,查询返回的结果集也是对应需要查询的数据库字段的值 /// </summary> /// <param name="sql">sql中指定了要查询的字段</param> /// <param name="fieldNames">数据库字段数组</param> /// <returns></returns> public String[] GetDatasFromSelectedTable(String sql,String[] fieldNames) { SqlDataAdapter sda = new SqlDataAdapter(sql, conn);//创建数据适配器对象 DataSet ds = new DataSet();//创建数据集 sda.Fill(ds);//填充数据集 ds.Dispose();//释放资源 //MessageBox.Show(ds.Tables[0].Rows.Count+""); String[] dataResults=new String[fieldNames.Length]; if (ds.Tables[0].Rows.Count==0) { return null; } for (int i = 0; i < fieldNames.Length;i++ ) { dataResults[i] = ds.Tables[0].Rows[0][fieldNames[i]].ToString().Trim(); } return dataResults; } /// <summary> /// 使用此方法可以得到数据集 /// </summary> /// <param name="sql">SQL语句</param> /// <returns>方法返回数据集</returns> public DataSet GetTable(String sql) { SqlDataAdapter sda = new SqlDataAdapter(sql, conn);//创建数据适配器对象 DataSet ds = new DataSet();//创建数据集 sda.Fill(ds);//填充数据集 ds.Dispose();//释放资源 return ds;//返回数据集 } /// <summary> /// //绑定下拉列表 /// </summary> /// <param name="strTable">数据库表名</param> /// <param name="cb">ComboBox对象</param> /// <param name="i">指定数据列索引</param> public void BindDropdownlist(String strSQL, ComboBox cb, int i) { conn.Open();//打开数据库连接 SqlCommand cmd = new SqlCommand(strSQL, conn); SqlDataReader sdr = cmd.ExecuteReader();//得到数据读取器 while (sdr.Read()) { cb.Items.Add(sdr[i].ToString().Trim());//添加信息 } conn.Close();//关闭数据库连接 } } }
下面是CmmUtils类的代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; namespace SMS.utils { class CommonUtils { /// <summary> /// 为GetDatasFromSelectedTable方法提供sql参数 /// </summary> /// <param name="fieldNames">要查询数据库表的字段名</param> /// <param name="tempSQL">是拼接后的数据库语句的from后面的部分</param> /// <returns></returns> public String organizeSqlStatementWithFields(String[] fieldNames, String tempSQL) { StringBuilder strSql = new StringBuilder("select "); for (int i = 0; i < fieldNames.Length; i++) { if (i != fieldNames.Length - 1) { strSql.Append(fieldNames[i] + ","); } else { strSql.Append(fieldNames[i]); } } strSql.Append(tempSQL); return strSql.ToString(); } /// <summary> /// 获取系统当前的日期和时间 /// </summary> /// <returns></returns> public String getSystemCurrentTime() { StringBuilder sb = new StringBuilder(""); sb.Append(DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.DayOfWeek.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString()); return sb.ToString(); } //只能输入数字 public void onlyInputDigitNumber(object sender, KeyPressEventArgs e) { if (((int)e.KeyChar < 48 || (int)e.KeyChar > 57) && (int)e.KeyChar != 8) { e.Handled = true; } } //只能输入数字和小数点,小数点只能一位且不能在第一位 public void onlyInputDigitAndDotNumber(object sender, KeyPressEventArgs e,TextBox textBox) { if (e.KeyChar != 8 && e.KeyChar != 13 && e.KeyChar != 46 && !char.IsNumber(e.KeyChar)) { e.Handled = true; } if (e.KeyChar == 46 && textBox.Text.Length == 0) { e.Handled = true; return; } int a = 0; try { a = textBox.Text.ToString().Trim().Split('.').Length; } catch { } if (e.KeyChar == 46 && a > 1) { e.Handled = true; } } //只能下拉选择 public void onlyDropDownToSelect(object sender, KeyPressEventArgs e) { MessageBox.Show("请进行下拉选择而无需手动填写!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); e.Handled = true; } } }
(1)首先贴上没有解密的用户登录代码:
//登录事件 private void buttonLogin_Click(object sender, EventArgs e) { try { string userName = textBoxUserName.Text.Trim(); string userPwd = textBoxPwd.Text.Trim(); if (userName == "" || userPwd == "") { MessageBox.Show("用户名或密码不能为空!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { SqlConnection conn = DBConnection.MyConnection(); String sql = "select * from tb_employee where loginName='"+userName+"' and loginPassword='"+userPwd+"'"; SqlDataReader sdr=operate.getSQLData(sql, conn); sdr.Read(); //存在该用户名与密码 if (sdr.HasRows) { conn.Close(); this.Hide(); CommonMessage.userName = userName; CommonMessage.userPassword = userPwd; String[] selFieldNames = new String[]{ "powerName" }; String tempSQL=" from tb_employee,tb_powerType where tb_employee.powerId=tb_powerType.powerId and tb_employee.loginName='"+userName+"'"; String strSql=cmmUtils.organizeSqlStatementWithFields(selFieldNames, tempSQL); String[] dataResults = operate.GetDatasFromSelectedTable(strSql.ToString(), selFieldNames); CommonMessage.userPower = dataResults[0]; Console.Write(userName + "--" + userPwd + "--" + CommonMessage.userPower); frmMain fMain = new frmMain(); fMain.ShowDialog(); } else { //清空文本内容 textBoxUserName.Text = ""; textBoxPwd.Text = ""; MessageBox.Show("用户名或密码错误!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); frmLogin_Activated(sender, e); } } } catch (System.Exception ex) { MessageBox.Show(ex.Message + "\n请与管理员联系!", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
(2)接着贴上有MD5加解密的用户登录的代码:
//加密解密工具 MD5EncryptAndDecrypt md5 = new MD5EncryptAndDecrypt(); //登录事件 private void buttonLogin_Click(object sender, EventArgs e) { try { string userName = textBoxUserName.Text.Trim(); string userPwd = textBoxPwd.Text.Trim(); if (userName == "" || userPwd == "") { MessageBox.Show("用户名或密码不能为空!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { String[] loginFieldNames = new String[]{ "loginPassword","loginKey" }; String tempSQL = " from tb_employee where loginName='" + userName + "'"; String strSql = cmmUtils.organizeSqlStatementWithFields(loginFieldNames, tempSQL); Console.WriteLine(strSql); String[] dataResults = operate.GetDatasFromSelectedTable(strSql.ToString(), loginFieldNames); if (dataResults==null) { MessageBox.Show("该用户名不存在!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { //Console.WriteLine("密码密文: " + dataResults[0] + "\n密码密钥: " + dataResults[1]); String loginPasswordEncrypt = dataResults[0]; String loginKeyTemp = dataResults[1]; String loginKeyReal = loginKeyTemp.Substring(0, loginKeyTemp.IndexOf(CommonMessage.lastKeyStr)); //MessageBox.Show(loginKeyReal); String loginPasswordDecrypt = md5.MD5Decrypt(loginPasswordEncrypt, loginKeyReal); if (!userPwd.Equals(loginPasswordDecrypt)) { MessageBox.Show("用户密码错误!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { //MessageBox.Show("登陆成功!", "友情提示", MessageBoxButtons.OK, MessageBoxIcon.Information); this.Hide(); CommonMessage.userName = userName; CommonMessage.userPassword = userPwd; String[] selFieldNames = new String[]{ "powerName" }; String tempPowerSQL = " from tb_employee,tb_powerType where tb_employee.powerId=tb_powerType.powerId and tb_employee.loginName='" + userName + "'"; String strPowerSql = cmmUtils.organizeSqlStatementWithFields(selFieldNames, tempPowerSQL); String[] dataPowerResults = operate.GetDatasFromSelectedTable(strPowerSql.ToString(), selFieldNames); CommonMessage.userPower = dataPowerResults[0]; Console.Write(userName + "--" + userPwd + "--" + CommonMessage.userPower); frmMain fMain = new frmMain(); fMain.ShowDialog(); } } } } catch (System.Exception ex) { MessageBox.Show(ex.Message + "\n请与管理员联系!", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
其中,MD5EncryptAndDecrypt 功能包括:产生密钥,加密密码明文为密码密文,解密密码密文为密码明文。其源代码可以参考我的下一篇博客!
在这里需要说明的,本系统虽然是MD5,但还在密钥Key中还拼接上了一段自定义的字符串,在本系统中为“520lancy”,所以在解密的时候需要去掉这段自定义的字符串,截断的代码为上述代码的String
loginKeyReal = loginKeyTemp.Substring(0, loginKeyTemp.IndexOf(CommonMessage.lastKeyStr)); 其中CommonMessage.lastKeyStr即为自定义的那段字符串。
下面是效果!登录界面和登录过程的处理以及成功后的主界面!
下文将介绍MD5加密的简要原理以及我采用C#实现并将其应用在本系统的源代码!