本文将讲解对于先后端分离的项目,前端注册或登陆时如何保证用户密码安全传输到server端,最终存入数据库
加密真的有必要吗?
咱们先来看一看前端发起的ajax请求中,若是不对密码进行加密,会发生什么。
f12打开chrome开发者工具,找到请求,查看请求参数以下:css
若是你的协议是http,那么前端传给后端的密码差很少是裸奔状态,由于http传输的是明文,极可能在传输过程当中被窃听,假装或篡改。
那么,弄个https不就行了吗?
https的确可以极大增长网站的安全性,可是用https得先买证书(也有免费的),对于我的站点或者不想弄证书的状况下,那最起码也得对用户密码进行一下加密吧。前端
先看一下大致流程图,首先,咱们用工具生成公钥和私钥,将其放入server端,前端发起请求获取公钥,拿到公钥后对密码进行加密,而后将加密后的密码发送到server端,server端将用密钥解密,最后再用sha1加密密码,存入数据库。node
既然选择RSA加密,那么首先得有工具啊,常见的有openssl,但这里不介绍,感兴趣的请自行查阅,对于node而言,我介绍一个不错的库Node-RSA,咱们将用它来生成RSA公钥和密钥。ios
RSA是一种非对称加密算法,即由一个密钥
和一个公钥
构成的密钥对,经过密钥加密,公钥解密,或者经过公钥加密,密钥解密。其中,公钥能够公开,密钥必须保密
。git
用Node-RSA生成的公钥和密钥代码以下:github
const NodeRSA = require('node-rsa') const fs = require('fs') // Generate new 512bit-length key var key = new NodeRSA({b: 512}) key.setOptions({encryptionScheme: 'pkcs1'}) var privatePem = key.exportKey('pkcs1-private-pem') var publicDer = key.exportKey('pkcs8-public-der') var publicDerStr = publicDer.toString('base64') // 保存返回到前端的公钥 fs.writeFile('./pem/public.pem', publicDerStr, (err) => { if (err) throw err console.log('公钥已保存!') }) // 保存私钥 fs.writeFile('./pem/private.pem', privatePem, (err) => { if (err) throw err console.log('私钥已保存!') })
执行完成后,咱们将在根目录下获得公钥和私钥文件:ajax
注意:server端的公钥和密钥应该隔一段时间换一次,好比每次服务器重启时。算法
核心代码以下:chrome
<script src="https://cdn.bootcss.com/jsencrypt/2.3.1/jsencrypt.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script> <script> function reg() { axios({ method: 'post', url: 'http://127.0.0.1:3000/getPublicKey' }) .then(res => { let result = res.data // 从后端获取的公钥 String var publicPem = result // 用JSEncrypt对密码进行加密 var encrypt = new JSEncrypt() encrypt.setPublicKey(publicPem) var password = 'abc123' password = encrypt.encrypt(password) axios({ method: 'post', url: 'http://127.0.0.1:3000/reg', data: { password: password } }) .then(res => { let result = res.data console.log(result) }) .catch(error => { console.log(error) }) }) } </script>
前端将用到jsencrypt对其进行加密,详细用法请参考github。数据库
后端核心代码:
const express = require('express'); const crypto = require('crypto'); const fs = require('fs'); var privatePem = fs.readFileSync('./pem/private.pem'); var app = express(); app.use(express.json()); // CORS 注意:要放在处理路由前 function crossDomain(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); } app.use(crossDomain) app.use(function (req, res, next) { // 不加会报错 if (req.method === 'OPTIONS') { res.end('ok') return } switch (req.url) { case '/getPublicKey': let publicPem = fs.readFileSync('./pem/public.pem', 'utf-8') res.json(publicPem) break case '/reg': // 解密 var privateKey = fs.readFileSync('./pem/private.pem', 'utf8') var password = req.body.password var buffer2 = Buffer.from(password, 'base64') var decrypted = crypto.privateDecrypt( { key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING // 注意这里的常量值要设置为RSA_PKCS1_PADDING }, buffer2 ) console.log(decrypted.toString('utf8')) // sha1加密 var sha1 = crypto.createHash('sha1'); var password = sha1.update(decrypted).digest('hex'); console.log('输入到数据库中的密码是: ', password) // 存入数据库中 // store to db... res.end('reg ok') break } }) app.listen(3000, '127.0.0.1')
这里,我是用node自带模块crpto进行解密,固然,你也能够用Node-RSA的方法进行解密。
咱们再来看一看前端请求的密码信息:
这样一串字符,即使被他人获取,若是没有密钥,在必定程度上,他是没法知道你的密码的。
固然,关于网络安全是一个大话题,本篇只是对其中的一小部分进行介绍,欢迎留言讨论,但愿对您有帮助。