https://www.cnblogs.com/smh188/p/11533668.html(我是如何一步步编码完成万仓网ERP系统的(一)系统架构)

  https://www.cnblogs.com/smh188/p/11534451.html(我是如何一步步编码完成万仓网ERP系统的(二)前端框架)

  https://www.cnblogs.com/smh188/p/11535449.html(我是如何一步步编码完成万仓网ERP系统的(三)登录)

  https://www.cnblogs.com/smh188/p/11541033.html(我是如何一步步编码完成万仓网ERP系统的(四)登录的具体实现)

  https://www.cnblogs.com/smh188/p/11542310.html(我是如何一步步编码完成万仓网ERP系统的(五)产品库设计 1.产品类别)

  https://www.cnblogs.com/smh188/p/11546917.html(我是如何一步步编码完成万仓网ERP系统的(六)产品库设计 2.百度Ueditor编辑器)

  https://www.cnblogs.com/smh188/p/11572668.html(我是如何一步步编码完成万仓网ERP系统的(七)产品库设计 3.品牌图片跨域上传)

  https://www.cnblogs.com/smh188/p/11576543.html(我是如何一步步编码完成万仓网ERP系统的(八)产品库设计 4.品牌类别)

  https://www.cnblogs.com/smh188/p/11578185.html(我是如何一步步编码完成万仓网ERP系统的(九)产品库设计 5.产品属性项)

  https://www.cnblogs.com/smh188/p/11589264.html(我是如何一步步编码完成万仓网ERP系统的(十)产品库设计 6.属性项和类别关联)

  万仓网ERP系统不开源,准备做一个系列,讲一讲主要的技术点,这些技术点会有源代码。如果想看所有源代码,可以打道回府了,没必要再阅读下去了,浪费您宝贵的时间。

  首先用户进入到一个后台系统,肯定是先要登录,这篇咱们就说说登录(当然万仓网ERP系统开发的第一个页面并不是登录)。

  登录页面主要的是注意保护用户的密码不能被窃取,不能是明文,不能被穷举撞库(简单密码),那怎样才能实现这3个小目标呢?

  1.不能被窃取,可以使用证书(let's encrypt的免费证书)

  2.最好在前端加密后在通过网络进行传输。

  3.最好有大小字母、数字和特殊符号组成8位及以上密码,防止暴力破解。

  现在咱们主要说第2中情况,如何在前端使用js加密,后端.Net进行解密?密码学和TLS的知识不在本文的介绍范围之内,直接上硬核内容吧,前端使用基于X25519密钥交换(RSA密钥交换已经过时,最新的Tls1.3只有ecc椭圆曲线密钥交换这一种,x25519就属于ecc椭圆曲线的一种)的aes-128-gcm(加解密速度和安全与一身的加密方式,比上一版本aes-cbc加密安全,cpu硬件内置aes-gcm加密模块)加密,后端.net进行解密,验证用户名和密码是否正确。

  

  google的证书使用的就是基于X25519的AES_128_GCM证书,等于咱们现在手工用js+.net实现一个基于X25519的AES_128_GCM的证书。

  引用js插件

  https://github.com/brix/crypto-js   sha512.min.js 主要用于原始密码加密。

  https://github.com/bitwiseshiftleft/sjcl   sjcl.min.js 需要重新压缩一下,主要用于aes-gcm加密。

  https://github.com/gimer/curve25519nxt  curve.js 可重新压缩一下,主要用于X25519的密钥交换。

  前端代码:

function login() {   //声明一个椭圆曲线对象   var curve = new Curve25519();      //随机一个64位的hex数值,并转化为字节类型,做为x25519的私钥var privateKey = new Key25519(hexToBytes(randomWord(64)));

   //得到椭圆曲线的公钥var publickey = curve.genPub(privateKey).key;     //把前端的私钥传入到后端,后端计算得出公用的aes加密的密钥,同时得到后端的公钥$.post("/Login/GetX25519PublicKey", { publicKey: bytesToHex(publickey) }, function (data) {          //得到后端的公钥,结合前端的私钥,计算得出前端aes加密的密钥(前后端计算的密钥是一致的)var shareKey = bytesToHex(curve.genShared(privateKey, new Key25519(hexToBytes(data.PublicKey))).key);           //声明一个登录对象var loginUser = new Object();           //用户名loginUser.UserName = $.trim($("#txtUserName").val());      //密码两次sha384加密        loginUser.Password = sha384(sha384($.trim($("#txtPWD").val())));       //验证码     loginUser.ValidateCode = $.trim($("#txtValidateCode").val());

      //使用公用密钥shareKey进行ase加密(aes后边的参数介绍下mode有gcm和ccm模式,这里用gcm模式;ts长度gcm模式是128;iter轮询次数默认10000,咱们改为1000,10000次数太多了会卡顿;salt盐随机数,iv是向量)var aesData = sjcl.encrypt(shareKey, JSON.stringify(loginUser), { mode: 'gcm', ts: 128, iter: 1000, salt: sjcl.random.randomWords(4), iv: sjcl.random.randomWords(3) });            //传入加密后的login字符串,同时需要传入后端返回的key(用于后端方便查找对应的公用密钥)$.post("/Login/UserLogin", { loginUser: aesData, key: data.Key }, function (data) {if (data.Result == false)        {          alert("登录失败")} else {window.location = "/";}});});
}

//随机一定长度的hex数值
function randomWord(len) {var str = "",arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];for (var i = 0; i < len; i++) {var pos = Math.round(Math.random() * (arr.length - 1));str += arr[pos];}return str;
}

  后端代码(传入前端的公钥,生成aes公用密钥,返回后端的公钥):

       //后端得到前端传过来的公钥,生成aes公用密钥,使用的是 BouncyCastle 类库   public static Dictionary<string, string> GenerateX25519Keys(string publicKey){SecureRandom secureRandom = new SecureRandom();byte[] privateByte = new byte[X25519.ScalarSize];byte[] publicByte = new byte[X25519.PointSize];byte[] shareByte = new byte[X25519.PointSize];secureRandom.NextBytes(privateByte);X25519.ScalarMultBase(privateByte, 0, publicByte, 0);X25519.ScalarMult(privateByte, 0, Hex.Decode(publicKey), 0, shareByte, 0);Dictionary<string, string> dc = new Dictionary<string, string>();//生成x25519 hex公钥dc.Add("PublicKey", Hex.ToHexString(publicByte));//生成x25519 hex私钥dc.Add("PrivateKey", Hex.ToHexString(privateByte));//生成aes公用密钥dc.Add("ShareKey", Hex.ToHexString(shareByte));//返回一个dictionary对象return dc;}public ActionResult GetX25519PublicKey(string publicKey){//得到x25519keyDictionary<string, string> eccKeys = GenerateX25519Keys(publicKey);               //RedisNewtonsoftSerializer serializer = new NewtonsoftSerializer();RedisConfiguration redisConfiguration = RedisCachingSectionHandler.GetConfig();IRedisCacheConnectionPoolManager connectionPoolManager = new RedisCacheConnectionPoolManager(redisConfiguration);IRedisCacheClient redisClient = new RedisCacheClient(connectionPoolManager, serializer, redisConfiguration);//使用guid keystring key = Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n");//把x25519字典用redis缓存起来redisClient.GetDbFromConfiguration().Add(key, eccKeys, DateTime.Now.AddSeconds(30));//返回guid key和x25519 公钥return Json(new { Key = key, PublicKey = eccKeys["PublicKey"] });} 

  后端代码(解密前端传过来的aes密文):

        //根据aes公用key 解密前端传进来的aes密文,使用的是 BouncyCastle 类库     //传进来的密文 {"iv":"r5idcq/NZ7VEpZv9","v":1,"iter":1000,"ks":128,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"rEwzN7o5UANsLvB4xf4bZg==","ct":"YI5Ubuvev8787ryV4+X+/1+ICXixZfqkhRxKg0zfi/27M24+Y8w9BOeIhe0tTEa1B1WP8tPYpcTYTHw58G/rpZUxSPNurhUXaAZKoiigl5eeaqOqNq9xHd0s+mKi+l1zuiL3qo5sxb0OcDxuL0clp46UyN0y8gr6xmimuszXalWdssfvCuoT8saJ4rwrcmM2TrjBMP/HG96VjAEzBD1q+teHFWJ50q4PLw=="} public static string DecryptString(string ciphertext, string key){//把aes密文转换为json对象JObject aesJObject = JObject.Parse(ciphertext);//把json对象的ct属性转换为字节(ct就是加密后的密文)byte[] ciphertextByte = Convert.FromBase64String(aesJObject["ct"].ToString());//把json的salt属性转换为字节byte[] salt = Convert.FromBase64String(aesJObject["salt"].ToString());//把json的iv属性转换为字节byte[] iv = Convert.FromBase64String(aesJObject["iv"].ToString());//声明一个PBKDF2对象Pkcs5S2ParametersGenerator pbkdf2 = new Pkcs5S2ParametersGenerator(new Sha256Digest());//根据aes公用key,salt,iter轮询次数(前面传进来的是1000,这里也是1000)初始化PBKDF2对象
            pbkdf2.Init(Encoding.UTF8.GetBytes(key), salt, 1000);byte[] keyByte = ((KeyParameter)pbkdf2.GenerateDerivedMacParameters(16 * 8)).GetKey();// 解密得到字符串return Encoding.UTF8.GetString(Decrypt(ciphertextByte, keyByte, iv));}//解密aes密文public static byte[] Decrypt(byte[] ciphertext, byte[] key, byte[] iv){//声明aes gcm对象GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());KeyParameter keyParam = ParameterUtilities.CreateKeyParameter("AES", key);//根据key和IV初始化ParametersWithIV cipherParameters = new ParametersWithIV(keyParam, iv);cipher.Init(false, cipherParameters);//解密byte[] plaintext = new byte[cipher.GetOutputSize(ciphertext.Length)];int length = cipher.ProcessBytes(ciphertext, 0, ciphertext.Length, plaintext, 0);cipher.DoFinal(plaintext, length);//返还前端加密前的login字节return plaintext;}}//解密aes login密文public ActionResult UserLogin(string loginUser, string key){      try{//Redis相关NewtonsoftSerializer serializer = new NewtonsoftSerializer();RedisConfiguration redisConfiguration = RedisCachingSectionHandler.GetConfig();IRedisCacheConnectionPoolManager connectionPoolManager = new RedisCacheConnectionPoolManager(redisConfiguration);IRedisCacheClient redisClient = new RedisCacheClient(connectionPoolManager, serializer, redisConfiguration);//使用传进来的来 Key获取Redis缓存的x25519 DictionaryDictionary<string, string> eccKey = redisClient.GetDbFromConfiguration().Get<Dictionary<string, string>>(key);if (eccKey == null){return;}//移除redis中x25519
                redisClient.GetDbFromConfiguration().Remove(key);//解密得到前端的login对象LoginViewModel loginUserView = JsonConvert.DeserializeObject<LoginViewModel>(DecryptString(loginUser, eccKey["ShareKey"]));//业务逻辑// ...return Json(new { Result = true });}catch (Exception ex){logError.Error(ex);          return ;}}

  这样一个还算完整的登录就算完成了,有兴趣的可以自己敲敲代码,做个小测试。

PS:客官有时间光临我的小站 万仓网

转载于:https://www.cnblogs.com/smh188/p/11535449.html

我是如何一步步编码完成万仓网ERP系统的(三)登录相关推荐

  1. SQL注入—我是如何一步步攻破一家互联网公司的

    最近在研究Web安全相关的知识,特别是SQL注入类的相关知识.接触了一些与SQL注入相关的工具.周末在家闲着无聊,想把平时学的东东结合起来攻击一下身边某个小伙伴去的公司,看看能不能得逞.不试不知道,一 ...

  2. AI一分钟 | 万达网科裁员95%高达5000余人,被爆下一步将转型AI; 英伟达放话了:研究人员放心用,不更新驱动就没啥事儿

    一分钟AI 万达网科裁员95% 后业务将转型AI,朱战备或接班曲德君成为新一任总裁 2020年东京奥运会将首次采用人脸识别系统,将在场馆入口处识别运动员.奥委会官员和记者等身份 意大利机器人公司E-N ...

  3. ASP.NET典型三层架构企业级医药行业ERP系统实战(8大模块22个子系统,价值3000万)...

    课程讲师:Tiger     课程分类:.net         适合人群:高级       课时数量:192课时       更新程度:完毕 我这里有一套课程和大家分享,我的qq是205905533 ...

  4. ASP.NET典型三层架构企业级医药行业ERP系统实战(8大模块22个子系统,价值3000万)

    ASP.NET典型三层架构企业级医药行业ERP系统实战(8大模块22个子系统,价值3000万) 课程讲师:Tiger     课程分类:.net         适合人群:高级       课时数量: ...

  5. 感谢万商网《万商访谈》栏目对我的专访报导

    又到了烟花三月下扬州的季节,但 07 年的早春二月不同于以往,电子商务热潮正席卷着整个中国大地,回顾近三年来走过的日子,就像肖邦夜曲般恬静而激情澎湃 -- 万商网记者叶小姐来我公司做客采访,将我作为& ...

  6. 如何看创建媒体日期_每天约4万个网约车投诉,看AI如何接招_媒体_澎湃新闻

    原创 科技日报 科技日报 ◎ 科技日报记者 张佳星 "我们的平台每天接待大概4万个投诉."10月29日,在"智能出行.引领未来"媒体沟通会上,首汽约车CEO魏东 ...

  7. 业界动态-新一代万亿级消息系统Pulsar的应用实践-03

    业界动态-新一代万亿级消息系统Pulsar的应用实践-03 Bookies

  8. 【网络工程】9、实操-万达酒店综合项目(三)

    接上篇<8.实操-万达酒店综合项目(三)> 之前我们按照项目要求进行模拟拓扑的构建实操,完成了办公区部分的网络配置,本篇我们来继续完成其他区域的网络配置. 一.总体架构情况 按照之前项目需 ...

  9. php云仓微商源码_微商云仓APP模式系统开发

    微商云仓微商电商系统开发,微商云仓系统模式开发搭建APP,微商云仓APP模式系统开发,微商云仓系统定制源码开发,微商云仓拿货系统开发,微商云仓类系统开发,微商云仓平台系统定制搭建开发,微商云仓商城AP ...

最新文章

  1. TTCN手动测试总结
  2. 关于数据事实表汇总的模拟实现——原理
  3. Codeforces Divisibility【水题】
  4. Fib数模n的循环节
  5. Web 绘图—服务器端绘图
  6. 解决Ubuntu下切换到root用户后没有声音问题
  7. Office 365 Sway-移动设备推送利器
  8. Linux 进程虚拟地址空间布局
  9. IntelliJ IDEA 将 Maven 构建的 Java 项目打包
  10. java判断字符串不为空_Java判断字符串是否为空的方法
  11. em算法python代码_EM算法Python实战
  12. 美剧深度扫盲:有线电视台之风起云涌--之一(转载)
  13. 【Multisim仿真】74LS193+74LS138流水灯
  14. 用TA学吉他,下个双11再也不孤单-吉他音阶实战 (三)
  15. isilon域环境配置
  16. elasticsearch实战 中文+拼音搜索
  17. 隐匿函数,二分法 冒泡排序
  18. (精)广东工业大学 2018实时大数据分析——ShinglingMinhash实验报告
  19. java servlet 教程_Java Servlet完全教程
  20. 初学者的图片SEO指南 - 为搜索引擎优化图片

热门文章

  1. Dubbo 负载均衡配置
  2. 运维高手的36项修炼_从大学生到经理人的36项修炼
  3. 游戏化设计应重视用户动机思考
  4. julia调用slurm进行多节点运行
  5. 反向传播算法(过程及公式推导)
  6. 如何查看github趋势榜
  7. 深度学习,怎么知道你的训练数据真的够了?
  8. C++ MFC深入详解之----设置控件背景透明
  9. 南华大学第十五届ACM程序设计竞赛(重现赛) E 免费机票
  10. 京东商城陷翻新门 揭了谁的丑