我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?

我没有使用服务器端语言,所以我不能这样做。


当前回答

这将基于传入的任意数量的参数生成一致的哈希:

/**
 * Generates a hash from params passed in
 * @returns {string} hash based on params
 */
function fastHashParams() {
    var args = Array.prototype.slice.call(arguments).join('|');
    var hash = 0;
    if (args.length == 0) {
        return hash;
    }
    for (var i = 0; i < args.length; i++) {
        var char = args.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash = hash & hash; // Convert to 32bit integer
    }
    return String(hash);
}

fastHashParams('hello world')输出“990433808”

fastHashParams('this',1,'has','lots','of','params',true)输出“1465480334”

其他回答

UUID v3和UUID v5实际上是给定输入字符串的散列。

UUID v3基于MD5,UUID v5基于SHA-1。

因此,最明显的选择是使用UUIDv5。

幸运的是,有一个流行的npm包,其中包括所有UUID算法。

npm install uuid

要实际生成UUIDv5,您需要一个唯一的命名空间。这个名称空间就像种子,应该是常量,以确保给定输入的输出始终相同。具有讽刺意味的是,您应该生成UUID v4作为命名空间。最简单的方法是使用一些在线工具。

一旦你有了一个名称空间,你就一切就绪了。

import { v5 as uuidv5 } from 'uuid';

const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
const hash = uuidv5('input', MY_NAMESPACE);

例如,如果输入字符串始终是URL,则可以使用一些默认名称空间。

const hashForURL = uuidv5('https://www.w3.org/', uuidv5.URL);

我有点惊讶,还没有人谈论新的SubtleCryptoAPI。

要从字符串中获取哈希,可以使用suble.desumest方法:

函数getHash(str,algo=“SHA-256”){let strBuf=newTextEncoder().encode(str);return crypto.define.digest(算法,strBuf).then(哈希=>{window.hash=哈希;//这里hash是arrayBuffer,//所以我们将其转换为十六进制版本let result=“”;const view=新数据视图(哈希);for(设i=0;i<hash.byteLength;i+=4){result+=('000000000'+view.getUint32(i).toString(16)).sslice(-8);}返回结果;});}getHash('hello world').then(哈希=>{console.log(哈希);});

詹金斯一次一哈希非常好:

//Credits (modified code): Bob Jenkins (http://www.burtleburtle.net/bob/hash/doobs.html)
//See also: https://en.wikipedia.org/wiki/Jenkins_hash_function
//Takes a string of any size and returns an avalanching hash string of 8 hex characters.
function jenkinsOneAtATimeHash(keyString)
{
  let hash = 0;
  for (charIndex = 0; charIndex < keyString.length; ++charIndex)
  {
    hash += keyString.charCodeAt(charIndex);
    hash += hash << 10;
    hash ^= hash >> 6;
  }
  hash += hash << 3;
  hash ^= hash >> 11;
  //4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask.
  return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16)
};

示例:

jenkinsOneAtATimeHash('test')
"31c25ec1"
jenkinsOneAtATimeHash('a')
"ca2e9442"
jenkinsOneAtATimeHash('0')
"6e3c5c6b"

您还可以删除末尾的.toString(16)部分以生成数字:

jenkinsOneAtATimeHash2('test')
834821825
jenkinsOneAtATimeHash2('a')
3392050242
jenkinsOneAtATimeHash2('0')
1849449579

请注意,如果您不需要对字符串或键进行哈希,而只需要凭空生成哈希,则可以使用:

window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)

示例:

window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"6ba9ea7"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"13fe7edf"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"971ffed4"

与上面相同,您可以删除末尾的`.toString(16)部分以生成数字:

window.crypto.getRandomValues(new Uint32Array(1))[0]
1154752776
window.crypto.getRandomValues(new Uint32Array(1))[0]
3420298692
window.crypto.getRandomValues(new Uint32Array(1))[0]
1781389127

注意:您也可以使用此方法一次生成多个值,例如:

window.crypto.getRandomValues(new Uint32Array(3))
Uint32Array(3) [ 2050530949, 3280127172, 3001752815 ]

我尝试了将字符代码转换为十六进制字符串的简单串联。这有一个相对狭窄的目的,即只需要与服务器端交换SHORT字符串(例如标题、标签)的哈希表示,因为不相关的原因,服务器端无法轻松实现接受的hashCode Java端口。显然,这里没有安全应用程序。

String.prototype.hash = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.map.call(range, function(i) {
    return self.charCodeAt(i).toString(16);
  }).join('');
}

这可以通过Undercore变得更加简洁和浏览器宽容。例子:

"Lorem Ipsum".hash()
"4c6f72656d20497073756d"

我想,如果你想以类似的方式对更大的字符串进行散列,你可以减少字符代码并对结果进行十六进制,而不是将单个字符连接在一起:

String.prototype.hashLarge = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.reduce.call(range, function(sum, i) {
    return sum + self.charCodeAt(i);
  }, 0).toString(16);
}

'One time, I hired a monkey to take notes for me in class. I would just sit back with my mind completely blank while the monkey scribbled on little pieces of paper. At the end of the week, the teacher said, "Class, I want you to write a paper using your notes." So I wrote a paper that said, "Hello! My name is Bingo! I like to climb on things! Can I have a banana? Eek, eek!" I got an F. When I told my mom about it, she said, "I told you, never trust a monkey!"'.hashLarge()
"9ce7"

当然,与此方法冲突的风险更大,尽管您可以在reduce中摆弄算法,但您希望多样化并延长哈希。

得益于mar10的示例,我找到了一种在C#和Javascript中为FNV-1a获得相同结果的方法。如果存在unicode字符,为了提高性能,将放弃上面的部分。不知道为什么在哈希时维护这些路径会很有用,因为我现在只哈希url路径。

C#版本

private static readonly UInt32 FNV_OFFSET_32 = 0x811c9dc5;   // 2166136261
private static readonly UInt32 FNV_PRIME_32 = 0x1000193;     // 16777619

// Unsigned 32bit integer FNV-1a
public static UInt32 HashFnv32u(this string s)
{
    // byte[] arr = Encoding.UTF8.GetBytes(s);      // 8 bit expanded unicode array
    char[] arr = s.ToCharArray();                   // 16 bit unicode is native .net 

    UInt32 hash = FNV_OFFSET_32;
    for (var i = 0; i < s.Length; i++)
    {
        // Strips unicode bits, only the lower 8 bits of the values are used
        hash = hash ^ unchecked((byte)(arr[i] & 0xFF));
        hash = hash * FNV_PRIME_32;
    }
    return hash;
}

// Signed hash for storing in SQL Server
public static Int32 HashFnv32s(this string s)
{
    return unchecked((int)s.HashFnv32u());
}

JavaScript版本

var utils = utils || {};

utils.FNV_OFFSET_32 = 0x811c9dc5;

utils.hashFnv32a = function (input) {
    var hval = utils.FNV_OFFSET_32;

    // Strips unicode bits, only the lower 8 bits of the values are used
    for (var i = 0; i < input.length; i++) {
        hval = hval ^ (input.charCodeAt(i) & 0xFF);
        hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
    }

    return hval >>> 0;
}

utils.toHex = function (val) {
    return ("0000000" + (val >>> 0).toString(16)).substr(-8);
}