在做注册功能时,之前一直没有考虑过加密问题,其实在控制台network这块儿是能看到明文的,所以为了安全,是要加密传输的

流程:

  • 保存密码:前端加密,传给后端,后段解密是否能解密,能就把前端传输的密文保存到数据库(注册)
  • 登录:前端加密后,传输后 端 ,后段解密,取数据库密码解密,两个解密后的对比一致则为密码正确 (登录)
  • 传输过程中和数据库里都是看不到明文的

首先,先说非对称加密的概念

其次,RSA对相同内容加密,每次返回的值都是不一样的,这就要求公钥加密,然后私钥解密获取到明文,保证前端传输的内容与后端接收到的内容相同

步骤:

后端:我用的是node.js,以此为例
const NodeRSA = require('node-rsa');//npm安装rsa模块
const fs = require('fs');
//生成公钥
function generator() {
    var key = new NodeRSA({ b: 512 })
    key.setOptions({ encryptionScheme: 'pkcs1' })
//在根目录下创建pem文件夹,fs模块后面会写入公钥跟私钥的txt文本
    var privatePem = key.exportKey('pkcs1-private-pem')
    var publicPem = key.exportKey('pkcs8-public-pem')

    fs.writeFile('./pem/public.pem', publicPem, (err) => {
        if (err) throw err
        console.log('公钥已保存!')
    })
    fs.writeFile('./pem/private.pem', privatePem, (err) => {
        if (err) throw err
        console.log('私钥已保存!')
    })
}
generator();

公钥私钥生成成功,接着定义前端获取公钥的接口

//获取公钥
app.get('/getPublicKey', (req, res) => {
    try {
        let publicKey = fs.readFileSync('./pem/public.pem', 'utf-8');
        console.log('publicKey', publicKey)
        res.send({ 'status': 0, 'msg': '公钥获取成功', 'resultmap': publicKey });
    } catch (err) {
        res.send(err);
    }
})
//成功的话,前端能拿到公钥

回到前端: 这里我用的是 Vue.js

import { JSEncrypt } from "jsencrypt"; //先npm安装jsencrypt,再在注册页引入
...//前端验证,如邮箱格式,两次输入的密码是否一致等
if(...){
//前端验证通过,接下来获取后端的公钥
 this.$axios.get("api/getPublicKey").then(res => { //api为config下Index跨域重写的接口
          if (res.data.status === 0) {
            let encryptor = new JSEncrypt(); //实例化
            encryptor.setPublicKey(res.data.resultmap); //设置公钥

           let registerData = { //传递给后端的注册内容
              username: this.username,
              password: encryptor.encrypt(this.pass2),//对密码进行rsa加密
              e_mail: this.e_mail
            };
        console.log(this.password)//查看下密码是否已加密
this.$axios
            .post("api/userRegister", registerData)
            .then(res => {
        //对返回值进行判断,是否用户名等重复
}
}

再回到后端

app.post("/userRegister", function (req, res) {
   let { username, e_mail } = req.body; //获取非加密用户名和邮箱
    MongoClient.connect(DBurl, function (err, db) {
        db.collection("register").findOne({
            username
        }, function (er, rs) {
            if (rs) {
                console.log(-1)//用户名重复-1
                res.send('-1')
            }
            else {

                db.collection("register").findOne({ e_mail }, function (e, r) {
                    if (r) {
                        //邮箱重复0
                        res.send('0')
                    }
                    else {
//这里对前端传来的密码进行解密
                        const privateKey = fs.readFileSync('./pem/private.pem', 'utf8'); //读取私钥
                        let buffer1 = Buffer.from(req.body.password, 'base64'); //转化格式
                        let password = crypto.privateDecrypt({
                            key: privateKey,
                            padding: crypto.constants.RSA_PKCS1_PADDING // 注意这里的常量值要设置为RSA_PKCS1_PADDING
                        },  buffer1).toString('utf8');
                        console.log('解密之后的密码', password);
 let b_password = crypto.createHmac('sha256',secret).update(password).digest("hex")//后端加密
                        db.collection("register").insertOne({
                            username, b_password, e_mail
                        }, function (e, r) {
                            if (r.insertedId) {
                                //插入成功1
                                console.log('插入成功1')
                                res.send("1")
                            }
                            else {
                                console.log('插入失败2')
                                res.send("2")
                            }
                        })
                    }
                })
            }
        })
    })
})

爬。