vlambda博客
学习文章列表

[js进阶]js自带的对称加密

前言

在登陆界面开发中,用户勾选了记住账号密码,账号密码信息被存储到cookieslocalStorage中,此时我们不希望它是以明文的方式存储的,需要对其进行加密,同时又需要在下次用户登陆时从本地取出账号密码信息并解密,这便需要使用对称加密算法,通常使用的加密算法有DES 算法等。

下载第三方的对称加密包,例如CryptoJS,似乎又会增加打包的体积,因为我们的需求很简单,仅仅只是实现简单的对称加密,window对象中的btoa,atob函数能很轻便的完成这件事。

WindowOrWorkerGlobalScope.btoa()

WindowOrWorkerGlobalScope.btoa()String 对象中创建一个 base-64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为一个二进制数据字节。

Note: 由于这个函数将每个字符视为二进制数据的字节,而不管实际组成字符的字节数是多少,所以如果任何字符的码位 (en-US)超出 0x00 ~ 0xFF 这个范围,则会引发 InvalidCharacterError 异常。请参阅 Unicode_字符串 ,该示例演示如何编码含有码位超出 0x00 ~ 0xFF 范围的字符的字符串。

语法

let encodedData = window.btoa(stringToEncode);

参数

  • stringToEncode

    一个字符串, 其字符分别表示要编码为 ASCII 的二进制数据的单个字节。

返回值

一个包含 stringToEncode 的 Base64 表示的字符串。

示例

let encodedData = window.btoa("Hello, world"); // 编码
let decodedData = window.atob(encodedData);    // 解码

备注

你可以使用此方法对可能导致通信问题的数据进行编码,传输,然后使用 atob() 方法再次解码数据。例如,可以编码控制字符,包括 ASCII 值为 0 到 31 的字符。

在用 JavaScript 编写 XPCOM 组件时,btoa() 方法也是可用的,虽然全局对象已经不是 Window 了。

Unicode 字符串

在多数浏览器中,使用 btoa() 对 Unicode 字符串进行编码都会触发 InvalidCharacterError 异常。

一种选择是转义任何扩展字符,以便实际编码的字符串是原始字符的 ASCII 表示形式。考虑这个例子,代码来自 Johan Sundström:

// ucs-2 string to base64 encoded ascii
function utoa(str{
    return window.btoa(unescape(encodeURIComponent(str)));
}
// base64 encoded ascii to ucs-2 string
function atou(str{
    return decodeURIComponent(escape(window.atob(str)));
}
// Usage:
utoa('✓ à la mode'); // 4pyTIMOgIGxhIG1vZGU=
atou('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"

utoa('I \u2661 Unicode!'); // SSDimaEgVW5pY29kZSE=
atou('SSDimaEgVW5pY29kZSE='); // "I ♡ Unicode!"

更好、更可靠、性能更优异的解决方案是使用类型化数组进行转换。

规范

规范 状态 备注
HTML Living Standard WindowOrWorkerGlobalScope.btoa() Living Standard Method moved to the WindowOrWorkerGlobalScope mixin in the latest spec.
HTML 5.1 WindowBase64.btoa() Recommendation Snapshot of HTML Living Standard. No change.
HTML5 WindowBase64.btoa() Recommendation Snapshot of HTML Living Standard. Creation of WindowBase64 (properties where on the target before it).

Polyfill

// Polyfill from  https://github.com/MaxArt2501/base64-js/blob/master/base64.js
(function({
    // base64 character set, plus padding character (=)
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        // Regular expression to check formal correctness of base64 encoded strings
        b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;

    window.btoa = window.btoa || function(string{
        string = String(string);
        var bitmap, a, b, c,
            result = "",
            i = 0,
            rest = string.length % 3// To determine the final padding

        for (; i < string.length;) {
            if ((a = string.charCodeAt(i++)) > 255 ||
                (b = string.charCodeAt(i++)) > 255 ||
                (c = string.charCodeAt(i++)) > 255)
                throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");

            bitmap = (a << 16) | (b << 8) | c;
            result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) +
                b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
        }

        // If there's need of padding, replace the last 'A's with equal signs
        return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
    };

    window.atob = window.atob || function(string{
        // atob can work with strings with whitespaces, even inside the encoded part,
        // but only \t, \n, \f, \r and ' ', which can be stripped.
        string = String(string).replace(/[\t\n\f\r ]+/g"");
        if (!b64re.test(string))
            throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");

        // Adding the padding if missing, for semplicity
        string += "==".slice(2 - (string.length & 3));
        var bitmap, result = "",
            r1, r2, i = 0;
        for (; i < string.length;) {
            bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 |
                (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));

            result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) :
                r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) :
                String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
        }
        return result;
    };
})()

参考

  • Base64 encoding and decoding
  • data URI
  • atob()
  • Components.utils.importGlobalProperties