之前已经将后端的加解密接口都准备好了过后、就可以跟前端进行联调了。这里使用了VUE作为前端开发框架。我们直接上关键代码了。

jsencrypt(RSA)加密

        这里使用了jsencrypt作为前端RSA的加解密工具。需要注意的是填充方式要与服务端的保持一致、我们服务端使用的填充方式是RSA/ECB/OAEPWithSHA-1AndMGF1PADDING。那么对应这里也应该是同样的填充方式。我们先npm安装一下

npm install jsencrypt --save

        添加加密方法(encryptUtils.js)

import jsrsasign from 'jsrsasign'
export default {
  /* 加密 */
  rsaPublicData(data,publicKeys) {
    console.log("进入加密方法"+data);
    data = encodeURIComponent(data);
    let ppKey = '-----BEGIN PUBLIC KEY-----';
    ppKey += publicKeys;
    ppKey += '-----END PUBLIC KEY-----';
    const pubKey = jsrsasign.KEYUTIL.getKey(ppKey);
    let singResult = jsrsasign.KJUR.crypto.Cipher.encrypt(data, pubKey, "RSAOAEP")
    singResult = jsrsasign.hextob64(singResult);
    console.log("sign加密结果是"+singResult);
    return singResult
  }
}

这里需要注意的是,刚刚说的填充方式、jsrsasign.KJUR.crypto.Cipher.encrypt(data, pubKey, "RSAOAEP256")。这里的RSAOAEP256对应的就是RSA/ECB/OAEPWithSHA-1AndMGF1PADDING的方式,具体对应可以看jsrsasign的APIjsrsasign JavaScript API Reference - KJUR.crypto.Cipher

将js引入main.js中,方便调用

import RSAUtils from "./utils/encryptUtils.js";
Vue.prototype.RSAUtils=RSAUtils

 这样就可以直接在要使用的地方调用

this.RSAUtils.rsaPublicData("要加密的内容","公钥");

AES加密

        AES采用的是crypto-js来进行加密。这里需要注意的是、我们采用的是AES的CBC模式、除了密钥之外还有一个向量。这样的加密强度要高很多、AES的普通ECB模式已经不安全了的。大家如果在使用ECB模式的、请尽快替换成CBC模式。这个插件也需要安装

npm install crypto-js --save-dev

添加加解密方法(AES.js)

import CryptoJS from 'crypto-js'
export default {
  //加密
  encrypt(word,temp_keyStrs,temp_Iv){
    // cbc加密
    let key = temp_keyStrs;
    let iv = temp_Iv;
    key = CryptoJS.enc.Utf8.parse(key);
    iv = CryptoJS.enc.Utf8.parse(iv);
    let srcs = CryptoJS.enc.Utf8.parse(word);
    // 加密模式为CBC
    let encrypted = CryptoJS.AES.encrypt(srcs, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
    });
    //返回base64
    return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
  },
  //解密
  decrypt(word,temp_keyStrs,temp_Iv){
       let key = temp_keyStrs;
        let iv = temp_Iv;
        key = CryptoJS.enc.Utf8.parse(key);
        iv = CryptoJS.enc.Utf8.parse(iv);
        let base64 = CryptoJS.enc.Base64.parse(word);
        let src = CryptoJS.enc.Base64.stringify(base64);
        // 解密模式为CBC,补码方式为PKCS5Padding(也就是PKCS7)
        let decrypt = CryptoJS.AES.decrypt(src, key, {
                iv: iv,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
        });
        let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
        return decryptedStr.toString();
  }
}

然后跟RSA一样,在main.js引入一下

import AES from "./utils/AES.js";
Vue.prototype.AES=AES

这样就可以在要使用的地方直接使用了

this.AES.encrypt("要加密的明文","aes密钥","aes向量")
this.AES.decrypt("要解密的密文","aes密钥","aes向量")

登录demo

模拟写一个登录功能。我们登录要分几步来走:

        1、获取临时Token;

        2、根据临时Token去获取RSA公钥(之前提到过、网关需要存储客户端的私钥用于解密就必须要分清楚当前请求到底是哪个客户端的,然后才能使用相应的私钥去解密);

        3、本地使用AES生成密钥跟向量;

        4、使用RSA公钥对AES密钥跟向量加密——并将结果写入请求头;

        5、使用AES密钥跟向量加密请求体内容;

        6、发送请求;

vue封装axios请求api.js

import axios from 'axios'
import {Message, MessageBox} from 'element-ui'
import {getToken} from '@/utils/auth'
import store from '../store'




// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_URL, // api的base_url
  timeout: 1999995000                  // 请求超时时间2
})
// request拦截器
service.interceptors.request.use(config => {
 if(localStorage.getItem('token')!=null && localStorage.getItem('token')!="")
 {
   config.headers["authorization"] =localStorage.getItem('token'); //把token添加到请求头每次请求接口时候带上
 }


 if(localStorage.getItem('AscKey')!=null && localStorage.getItem('AscKey')!="")
 {
   config.headers["Requestsecret"] =localStorage.getItem('AscKey'); //把token添加到请求头每次请求接口时候带上
 }
  return config
}, error => {
  // Do something with request error
  console.error(error) // for debug
  Promise.reject(error)
})

export default service

完整的登录示例

export default {
    name: 'login',
    data() {
        return {
            mapSrc: "",
            loginForm: {
                userAccount: 'admin',
                userPass: '123456',
                imgCode: "ESEXRC",
                smsCode: "123456"
            },
            tokenInfo: {
                tempKey: "", //临时token
                token: ""  //长期token
            },
            raskey: {
                publicKey: "",
                privateKey: ""
            },
            loginRules: {
                username: [{required: true, trigger: 'blur', message: "请输入用户名"}],
                password: [{required: true, trigger: 'blur', message: "请输入密码"}]
            },
            aesKey: {
                key: "",
                iv: ""
            },
            loading: false
        }
    },
    created() {
        this.aesKey.key = this.createCode(16);
        this.aesKey.iv = this.createCode(16); //AES 向量
        this.get_temp_token();
    },
    methods: {
        /**
         * 获取临时token
         */
        get_temp_token() {
            console.log("获取临时token");
            this.api({
                url: "../临时token",
                method: "get"
            }).then(data => {
                this.tokenInfo.tempKey = data.tempToken;
                localStorage.setItem('token', 'Bearer ' + data.tempToken);//保存token
                this.getrsaKey();

            })
        },
        /**
         * 获取公钥
         */
        getrsaKey() {
            this.api({
                url: "../获取公钥",
                method: "get"
            }).then(data => {
                console.log("请求密钥对的结果是" + JSON.toString(data));
                this.raskey.publicKey = data.publicKey;
                this.createCode(12);
                localStorage.setItem('publicKey', data.publicKey);//保存token
                console.log("公钥是" + localStorage.getItem('publicKey'));
            })

        },
        /**
         * 登录
         */
        login(publicKeys) {
            var ASCKeys = this.aesKey.key + "@" + this.aesKey.iv;
            console.log("temp_keyStrs=" + this.aesKey.key);
            console.log("temp_Iv=" + this.aesKey.iv);
            console.log("加密前的值" + JSON.stringify(this.loginForm));
            var encrypts = this.AES.encrypt(JSON.stringify(this.loginForm), this.aesKey.key, this.aesKey.iv);
            console.log("加密后的值=" + encrypts);
            var decrypts = this.AES.decrypt(encrypts, this.aesKey.key, this.aesKey.iv);
            console.log("解密后" + decrypts);
            var jmResult = this.RSAUtils.rsaPublicData(ASCKeys, publicKeys); // 对称加密的keys
            localStorage.setItem('AscKey', jmResult); // 保存token
            var sendData = {"text": encrypts};
            this.api({
                url: "../登录",
                method: "post",
                data: sendData
            }).then(data => {
                console.log("登录请求得到的Token" + data.token);
                localStorage.setItem('token', 'Bearer ' + data.token); // 保存token ;
                this.$router.push({path: '/testmain'})
            })
        },
        /**
         * 获取图片验证码
         */
        changeImgCode() {
            this.api({
                url: "../获取验证码",
                method: "get",
                responseType: Blob,
            }).then(data => {
                let blob = new Blob([data]);   // 返回的文件流数据
                let url = window.URL.createObjectURL(blob);  // 将他转化为路径
                console.log("图片路径:" + url + "r\n");
            })
        },
        toRegister() {
            this.$router.push({path: '/register'})
        },
        createCode(strLength) {
            var code = '23456789AaBbCcDdEeFfGgHhJjKkLlMmNnPpOoQqRrSsTtUuVvWwXxYyZz1Ii';
            var tempCode = "";
            var tempSize = 0;
            //设置长度,这里看需求,我这里设置了4
            var codeLength = strLength;
            //设置随机字符
            var random = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
            //循环codeLength 我设置的4就是循环4次
            for (var i = 0; i < codeLength; i++) {
                //设置随机数范围,这设置为0 ~ 36
                var tempSize = Math.floor(this.getRandomArbitrary(0, code.length - 1));
                tempCode += code.substr(tempSize, 1);
            }
            return tempCode;
        },
        getRandomArbitrary(min, max) { //生成随机数
            return Math.random() * (max - min) + min;
        },
        handleLogin() {
            this.$refs.loginForm.validate(valid => {
                if (valid) {
                    this.loading = true
                    console.log("公钥是" + localStorage.getItem('publicKey'));
                    this.login(localStorage.getItem('publicKey'));
                } else {
                    return false
                }
            })
        }
    }
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐