最近闲来无事给自己写了家庭财务收支管理系统,也就包含支出管理,收入管理和一些统计功能。
先说登录模块,因为涉及GET和POST请求,这些东西都是能被监控和抓取的所以就考虑这使用RSA加密解密方式传输用户名和密码参数,页面JS如下:
1 /*需要引入三个JS文件,BigInt.js、RSA.js和Barrett.js,用到cookie则需要引入jquery.cookie.js文件*/ 2 //与后台交互获取公钥 3 function getPublicKey() { 4 var pubKey = ''; 5 if ($.cookie('publicKey') == null) { 6 $.ajax({ 7 url: "/Account/GetRsaPublicKey", 8 type: "get", 9 contentType: "application/x-www-form-urlencoded; charset=utf-8", 10 async: false, 11 data: {}, 12 dataType: "json", 13 success: function (data) { 14 if (data.Code == 0) { 15 pubKey = data.RsaPublicKey + "," + data.Key; 16 $.cookie('publicKey', pubKey, { expires: 1 / 1440 }); 17 } else { 18 Config.Method.JudgeCode(data, 1); 19 } 20 } 21 }); 22 } else { 23 pubKey = $.cookie('publicKey'); 24 } 25 return pubKey; 26 } 27 //公钥加密用户密码Pwd为RSA加密后参数 28 function rsaEncrypt(pwd) { 29 var publicKey = getPublicKey(); 30 setMaxDigits(129); 31 var rsaKey = new RSAKeyPair(publicKey.split(",")[0], "", publicKey.split(",")[1]); 32 var pwdRtn = encryptedString(rsaKey, pwd); 33 return pwdRtn + "," + publicKey.split(",")[2]; 34 } 35 //POST登录请求,参数 36 <script type="text/javascript"> 37 $(function () { 38 $('#btnSubmit').live('click', function () { 39 var uName = $('#u').val(); 40 var pwd = $('#p').val(); 41 if (uName == '') { 42 alert('用户名不能为空'); 43 return; 44 } 45 if (pwd == '') { 46 alert('用户密码不能为空'); 47 return; 48 } 49 var enPwd = rsaEncrypt(pwd); 50 $.ajax({ 51 type: "POST", 52 url: "/Account/UserLogin", 53 data: { 'UserName': uName, 'Pwd': enPwd.split(",")[0], 'Key': enPwd.split(",")[1], 'RUrl': $('#hiddenUrl').val() }, 54 contentType: "application/x-www-form-urlencoded; charset=utf-8", 55 async: false, 56 dataType: "json", 57 success: function (data) { 58 if (data.result == true) { 59 window.location.href = data.url; 60 return false; 61 } else { 62 $('#msg').text(data.message); 63 } 64 }, 65 error: function (XMLHttpRequest, textStatus, errorThrown) { 66 $('#msg').text(XMLHttpRequest.status + '||' + XMLHttpRequest.readyState + '||' + textStatus); 67 } 68 }); 69 }); 70 }) 71 </script>
前台加密完成后就需要后台做解密处理,解密完成后需要使用MD5加密现有密码与数据库中用户密码进行比较验证,如果验证通过则需要写入cookie以便下次用户能自 动登录,由于cookie中我不希望用户名和密码都明码存储,我这里用到了AES加密的方式,自定义一个32位的加密密钥对cookie进行加密解密处理,后台c#代码如 下:
1 [HttpPost] 2 public JsonResult UserLogin(string UserName, string Pwd, string Key, string RUrl) 3 { 4 string privateKey = Common.CacheGet(Key) as string; 5 if (!string.IsNullOrEmpty(privateKey)) 6 { 7 if (string.IsNullOrEmpty(UserName)) 8 { 9 return Json(new { result = false, message = "用户名为空" }, JsonRequestBehavior.AllowGet); 10 } 11 if (string.IsNullOrEmpty(Pwd)) 12 { 13 return Json(new { result = false, message = "用户密码为空" }, JsonRequestBehavior.AllowGet); 14 } 15 string pwd = Common.DecryptRSA(Pwd, privateKey);//私钥解密 16 string md5Pwd = Common.NoneEncrypt(Common.NoneEncrypt(Common.NoneEncrypt(pwd, 1), 1), 1);//将解密后的值md5加密3次 17 AccountUnserInfo userInfo = bll.GetUserInfo(UserName.Trim(), md5Pwd); 18 if (userInfo != null && userInfo.U_Id > 0)//用户信息存在 19 { 20 //用户名、密码放入cookie 21 HttpCookie cookie = new HttpCookie("fw_izz"); 22 //AES加密Cookie 23 cookie["u_name"] = AesEncryptHelper.EncryptAes(UserName); 24 cookie["u_pwd"] = AesEncryptHelper.EncryptAes(pwd); 25 cookie.Expires = DateTime.Now.AddDays(7); 26 Response.Cookies.Add(cookie); 27 if (!string.IsNullOrEmpty(RUrl))//接收隐藏域中的值 28 { 29 return Json(new { result = true, message = "成功", url = RUrl }); 30 } 31 else 32 { 33 return Json(new { result = true, message = "成功", url = "/AccountDetail/Index" }); 34 } 35 } 36 else 37 { 38 return Json(new { result = false, message = "用户信息不存在", url = "/Account/Index" }); 39 } 40 } 41 else 42 { 43 return Json(new { result = false, message = "非法秘钥", url = "/Account/Index" }); 44 } 45 }
各种加密解密方法、Cache操作以及cookie操作代码如下:
1 public class Common 2 { 3 /// <summary> 4 /// 产生一组RSA公钥、私钥 5 /// </summary> 6 /// <returns></returns> 7 public static Dictionary<string, string> CreateRsaKeyPair() 8 { 9 var keyPair = new Dictionary<string, string>(); 10 var rsaProvider = new RSACryptoServiceProvider(1024); 11 RSAParameters parameter = rsaProvider.ExportParameters(true); 12 keyPair.Add("PUBLIC", BytesToHexString(parameter.Exponent) + "," + BytesToHexString(parameter.Modulus)); 13 keyPair.Add("PRIVATE", rsaProvider.ToXmlString(true)); 14 return keyPair; 15 } 16 17 /// <summary> 18 /// RSA解密字符串 19 /// </summary> 20 /// <param name="encryptData">密文</param> 21 /// <param name="privateKey">私钥</param> 22 /// <returns>明文</returns> 23 public static string DecryptRSA(string encryptData, string privateKey) 24 { 25 string decryptData = ""; 26 try 27 { 28 var provider = new RSACryptoServiceProvider(); 29 provider.FromXmlString(privateKey); 30 31 byte[] result = provider.Decrypt(HexStringToBytes(encryptData), false); 32 ASCIIEncoding enc = new ASCIIEncoding(); 33 decryptData = enc.GetString(result); 34 } 35 catch (Exception e) 36 { 37 throw new Exception("RSA解密出错!", e); 38 } 39 return decryptData; 40 } 41 42 private static string BytesToHexString(byte[] input) 43 { 44 StringBuilder hexString = new StringBuilder(64); 45 46 for (int i = 0; i < input.Length; i++) 47 { 48 hexString.Append(String.Format("{0:X2}", input[i])); 49 } 50 return hexString.ToString(); 51 } 52 53 public static byte[] HexStringToBytes(string hex) 54 { 55 if (hex.Length == 0) 56 { 57 return new byte[] { 0 }; 58 } 59 if (hex.Length % 2 == 1) 60 { 61 hex = "0" + hex; 62 } 63 byte[] result = new byte[hex.Length / 2]; 64 for (int i = 0; i < hex.Length / 2; i++) 65 { 66 result[i] = byte.Parse(hex.Substring(2 * i, 2), System.Globalization.NumberStyles.AllowHexSpecifier); 67 } 68 return result; 69 } 70 71 private static ObjectCache Cache 72 { 73 get { return MemoryCache.Default; } 74 } 75 /// <summary> 76 /// 获取缓存 77 /// </summary> 78 /// <param name="key"></param> 79 /// <returns></returns> 80 public static object CacheGet(string key) 81 { 82 return Cache[key]; 83 } 84 /// <summary> 85 /// 设置缓存 86 /// </summary> 87 /// <param name="key"></param> 88 /// <param name="data"></param> 89 /// <param name="cacheTime"></param> 90 public static void CacheSet(string key, object data, int cacheTime) 91 { 92 CacheItemPolicy policy = new CacheItemPolicy(); 93 policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); 94 Cache.Add(new CacheItem(key, data), policy); 95 } 96 /// <summary> 97 /// 判断缓存是否存在 98 /// </summary> 99 /// <param name="key"></param> 100 /// <returns></returns> 101 public static bool IsSet(string key) 102 { 103 return (Cache[key] != null); 104 } 105 /// <summary> 106 /// 缓存失效 107 /// </summary> 108 /// <param name="key"></param> 109 public static void CacheRemove(string key) 110 { 111 Cache.Remove(key); 112 } 113 /// <summary> 114 /// 对字符串进行加密(不可逆) 115 /// </summary> 116 /// <param name="Password">要加密的字符串</param> 117 /// <param name="Format">加密方式,0 is SHA1,1 is MD5</param> 118 /// <returns></returns> 119 public static string NoneEncrypt(string Password, int Format) 120 { 121 string strResult = ""; 122 switch (Format) 123 { 124 case 0: 125 strResult = FormsAuthentication.HashPasswordForStoringInConfigFile(Password, "SHA1"); 126 break; 127 case 1: 128 strResult = FormsAuthentication.HashPasswordForStoringInConfigFile(Password, "MD5"); 129 break; 130 default: 131 strResult = Password; 132 break; 133 } 134 return strResult; 135 } 136 }