如何可靠地散列 JavaScript 对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5559712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to reliably hash JavaScript objects?
提问by nh2
Is there a reliable way to JSON.stringify a JavaScript object that guarantees that the ceated JSON string is the same across all browsers, node.js and so on, given that the Javascript object is the same?
有没有一种可靠的方法来 JSON.stringify 一个 JavaScript 对象,以保证 ceated JSON 字符串在所有浏览器、node.js 等中都是相同的,前提是 Javascript 对象是相同的?
I want to hash JS objects like
我想散列 JS 对象,例如
{
signed_data: object_to_sign,
signature: md5(JSON.stringify(object_to_sign) + secret_code)
}
and pass them around across web applications (e.g. Python and node.js) and the user so that the user can authenticate against one service and show the next service "signed data" for that one to check if the data is authentic.
并将它们在 Web 应用程序(例如 Python 和 node.js)和用户之间传递,以便用户可以针对一项服务进行身份验证并显示下一项服务的“签名数据”以检查该数据是否真实。
However, I came across the problem that JSON.stringify is not really unique across the implementations:
但是,我遇到了一个问题,即 JSON.stringify 在各个实现中并不是真正唯一的:
- In node.js / V8, JSON.stringify returns a JSON string without unnecessary whitespace, such as '{"user_id":3}.
- Python's simplejson.dumps leaves some whitespace, e.g. '{"user_id": 3}'
- Probably other stringify implementations might deal differently with whitespace, the order of attributes, or whatever.
- 在 node.js / V8 中,JSON.stringify 返回一个没有不必要空格的 JSON 字符串,例如 '{"user_id":3}。
- Python 的 simplejson.dumps 留下了一些空白,例如 '{"user_id": 3}'
- 可能其他 stringify 实现可能会以不同的方式处理空格、属性顺序或其他任何内容。
Is there a reliable cross-platform stringify method? Is there a "nomalised JSON"?
是否有可靠的跨平台字符串化方法?是否有“标准化的 JSON”?
Would you recommend other ways to hash objects like this?
你会推荐其他方法来散列这样的对象吗?
UPDATE:
更新:
This is what I use as a workaround:
这是我用作解决方法的方法:
normalised_json_data = JSON.stringify(object_to_sign)
{
signed_data: normalised_json_data,
signature: md5(normalised_json_data + secret_code)
}
So in this approach, not the object itself, but its JSON representation (which is specific to the sigining platform) is signed. This works well because what I sign now is an unambiguous string and I can easily JSON.parse the data after I have checked the signature hash.
所以在这种方法中,不是对象本身,而是它的 JSON 表示(特定于签名平台)被签名。这很有效,因为我现在签署的是一个明确的字符串,我可以在检查签名哈希后轻松 JSON.parse 数据。
The drawback here is that if I send the whole {signed_data, signature} object as JSON around as well, I have to call JSON.parse twice and it does not look as nice because the inner one gets escaped:
这里的缺点是,如果我也将整个 {signed_data, signature} 对象作为 JSON 发送,我必须调用 JSON.parse 两次,它看起来不太好,因为内部的被转义了:
{"signature": "1c3763890298f5711c8b2ea4eb4c8833", "signed_data": "{\"user_id\":5}"}
采纳答案by Mark Kahn
You're asking for an implementation of something across multiple languages to be the same... you're almost certainly out of luck. You have two options:
您要求跨多种语言实现相同的东西......您几乎肯定不走运。您有两个选择:
- check www.json.org implementations to see if they might be more standardized
- roll your own in each language (use json.org implementations as a base and there should be VERY little work to do)
- 检查 www.json.org 实现,看看它们是否可能更标准化
- 在每种语言中推出自己的语言(使用 json.org 实现作为基础,应该很少有工作要做)
回答by Frosty Z
You might be interested in npm package object-hash, which seems to have a rather good activity & reliability level.
您可能对 npm 包object-hash感兴趣,它似乎具有相当好的活动性和可靠性级别。
var hash = require('object-hash');
var testobj1 = {a: 1, b: 2};
var testobj2 = {b: 2, a: 1};
var testobj3 = {b: 2, a: "1"};
console.log(hash(testobj1)); // 214e9967a58b9eb94f4348d001233ab1b8b67a17
console.log(hash(testobj2)); // 214e9967a58b9eb94f4348d001233ab1b8b67a17
console.log(hash(testobj3)); // 4a575d3a96675c37ddcebabd8a1fea40bc19e862
回答by Matt Forster
This is an old question, but I thought I'd add a current solution to this question for any google referees.
这是一个老问题,但我想我会为任何谷歌裁判添加这个问题的当前解决方案。
The best way to sign and hash JSON objects now is to use JSON Web Tokens. This allows for an object to be signed, hashed and then verified by others based on the signature. It's offered for a bunch of different technologies and has an active development group.
现在签署和散列 JSON 对象的最佳方法是使用JSON Web Tokens。这允许对对象进行签名、散列,然后由其他人根据签名进行验证。它提供了一系列不同的技术,并有一个活跃的开发团队。
回答by Greg Hewgill
You could normalise the result of stringify()
by applying rules such as:
您可以stringify()
通过应用以下规则来规范化结果:
- remove unnecessary whitespace
- sort attribute names in hashes
- well-defined consistent quoting style
- normalise string contents (so "\u0041" and "A" become the same)
- 删除不必要的空格
- 对哈希中的属性名称进行排序
- 定义明确的一致引用风格
- 标准化字符串内容(因此“\u0041”和“A”变得相同)
This would leave you with a canonical JSON representation of your object, which you can then reliably hash.
这将为您留下对象的规范 JSON 表示,然后您可以可靠地对其进行散列。
回答by isgoed
After trying some hash algorithms and JSON-to-string methods, I found this to work the best (Sorry, it is typescript, can of course be rewritten to javascript):
在尝试了一些哈希算法和 JSON-to-string 方法后,我发现这个方法效果最好(抱歉,它是 typescript,当然可以重写为 javascript):
// From: https://stackoverflow.com/questions/5467129/sort-javascript-object-by-key
function sortObjectKeys(obj){
if(obj == null || obj == undefined){
return obj;
}
if(typeof obj != 'object'){ // it is a primitive: number/string (in an array)
return obj;
}
return Object.keys(obj).sort().reduce((acc,key)=>{
if (Array.isArray(obj[key])){
acc[key]=obj[key].map(sortObjectKeys);
}
else if (typeof obj[key] === 'object'){
acc[key]=sortObjectKeys(obj[key]);
}
else{
acc[key]=obj[key];
}
return acc;
},{});
}
let xxhash64_ObjectToUniqueStringNoWhiteSpace = function(Obj : any)
{
let SortedObject : any = sortObjectKeys(Obj);
let jsonstring = JSON.stringify(SortedObject, function(k, v) { return v === undefined ? "undef" : v; });
// Remove all whitespace
let jsonstringNoWhitespace :string = jsonstring.replace(/\s+/g, '');
let JSONBuffer: Buffer = Buffer.from(jsonstringNoWhitespace,'binary'); // encoding: encoding to use, optional. Default is 'utf8'
return xxhash.hash64(JSONBuffer, 0xCAFEBABE, "hex");
}
It used npm module: https://cyan4973.github.io/xxHash/, https://www.npmjs.com/package/xxhash
它使用的NPM模块:https://cyan4973.github.io/xxHash/,https://www.npmjs.com/package/xxhash
The benefits:
好处:
- This is deterministic
- Ignores key order (preserves array order)
- Cross platform (if you can find equivalents for JSON-stringify) JSON-stringify will hopefully will not get a different implementation and the whitespace removal will hopefully make it JSON-formatting independent.
- 64-bit
- Hexadecimal string a result
- Fastest (0.021 ms for 2177 B JSON, 2.64 ms for 150 kB JSON)
- 这是确定性的
- 忽略键顺序(保留数组顺序)
- 跨平台(如果你能找到 JSON-stringify 的等价物) JSON-stringify 有望不会得到不同的实现,并且空格去除有望使其独立于 JSON 格式。
- 64 位
- 十六进制字符串结果
- 最快(2177 B JSON 为 0.021 毫秒,150 kB JSON 为 2.64 毫秒)
回答by spiffytech
You may find bencodesuitable for your needs. It's cross-platform, and the encoding is guaranteed to be the same from every implementation.
您可能会发现bencode适合您的需求。它是跨平台的,并且保证每个实现的编码都是相同的。
The downside is it doesn't support nulls or booleans. But that may be okay for you if you do something like transforming e.g., bools -> 0|1
and nulls -> "null"
before encoding.
缺点是它不支持空值或布尔值。但是,如果您在编码之前执行诸如转换 bools ->0|1
和nulls -> 之类的操作,那对您来说可能没问题"null"
。