博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#:生成哈希字符串
阅读量:3529 次
发布时间:2019-05-20

本文共 5980 字,大约阅读时间需要 19 分钟。

目录


介绍

散列是将值转换为通常较短的固定长度的键/值,其表示原始值。几天前,我们不得不使用哈希比较来通过API在两个系统之间同步数据(显然,这不是使用API​​进行数据同步的最有效方式,但我们没有选择在源端添加任何更改)

背景

我们在做什么:

  1. 在对象JSON反序列化之后在我们的末尾创建一个哈希字符串
  2. 通过唯一标识符(主键)将该哈希字符串与现有数据库行进行比较
    1. 如果唯一标识符(主键)找不到行,则向DB添加新行
    2. 如果哈希字符串不相同,则使用新值更新现有行
  3. 还有其他几个同步日志进程

一切都按预期工作,直到我们重构现有代码(更改了一些模型和属性的名称)。哈希字符串是从整个对象(包括所有值)生成的,而不是考虑特定的属性。我们创建哈希字符串的方式实际上是错误的。我们来看几个哈希字符串示例。

哈希助手类

这是用于管理与哈希相关的操作的实用程序类。

using System.IO;using System.Linq;using System.Runtime.Serialization.Formatters.Binary;using System.Security.Cryptography;using System.Text;public class HashHelper{    ///     /// for custom class need [Serializable]    /// to ignore https://stackoverflow.com/questions/33489930/    /// ignore-non-serialized-property-in-binaryformatter-serialization    ///     ///     /// 
public byte[] Byte(object value) { /*https://stackoverflow.com/questions/1446547/ how-to-convert-an-object-to-a-byte-array-in-c-sharp*/ using (var ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, value == null ? "null" : value); return ms.ToArray(); } } public byte[] Hash(byte[] value) { /*https://support.microsoft.com/en-za/help/307020/ how-to-compute-and-compare-hash-values-by-using-visual-cs*/ /*https://andrewlock.net/why-is-string-gethashcode- different-each-time-i-run-my-program-in-net-core*/ byte[] result = MD5.Create().ComputeHash(value); return result; } public byte[] Combine(params byte[][] values) { /*https://stackoverflow.com/questions/415291/ best-way-to-combine-two-or-more-byte-arrays-in-c-sharp*/ byte[] rv = new byte[values.Sum(a => a.Length)]; int offset = 0; foreach (byte[] array in values) { System.Buffer.BlockCopy(array, 0, rv, offset, array.Length); offset += array.Length; } return rv; } public string String(byte[] hash) { /*https://stackoverflow.com/questions/1300890/ md5-hash-with-salt-for-keeping-password-in-db-in-c-sharp*/ StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { sb.Append(hash[i].ToString("x2")); /*do not make it X2*/ } var result = sb.ToString(); return result; } public byte[] Hash(params object[] values) { byte[][] bytes = new byte[values.Length][]; for(int i=0; i < values.Length; i++) { bytes[i] = Byte(values[i]); } byte[] combined = Combine(bytes); byte[] combinedHash = Hash(combined); return combinedHash; } /*https://stackoverflow.com/questions/5868438/c-sharp-generate-a-random-md5-hash*/ public string HashString(string value, Encoding encoding = null) { if (encoding == null) { encoding = Encoding.ASCII; } byte[] bytes = encoding.GetBytes(value); byte[] hash = Hash(bytes); string result = String(hash); return result; } public string HashString(params object[] values) { var hash = Hash(values); /*Add more not constant properties as needed*/ var value = String(hash); return value; }}

考虑

  • 使用MD5哈希Hash(byte[] value)
  • 任何null 值都被视为'null'字符串Byte(object value)

对象到哈希字符串的过程

  1. 创建该对象的字节 Byte(object value)
  2. 从对象字节Hash(byte[] value)创建哈希字节 
  3. 散列字节String(byte[] hash)中返回的字符串 

多个对象组合的哈希

  1. 创建每个对象的字节 Byte(object value)
  2. 将字节组合或求和 Combine(params byte[][] values)
  3. 从组合创建哈希字节或对字节求和 Hash(byte[] value)
  4. 散列字节String(byte[] hash)中返回的字符串 

或者:

  1. 创建组合哈希字节 Hash(params object[] values)
  2. 散列字节String(byte[] hash)中返回的字符串 

我们将更频繁地使用的方法

  • 创建任何字符串的哈希字符串 HashString(string value, Encoding encoding = null)
  • 创建任何/对象组的哈希/组合哈希字符串 HashString(params object[] values)

整个对象的哈希

数据类或模型:

[Serializable]class PeopleModel{    public long? Id { get; set; }    public string Name { get; set; }    public bool? IsActive { get; set; }    public DateTime? CreatedDateTime { get; set; }}

创建模型的哈希:

/*9105d073ad276d742c56a049abd4ddef * will change if we change  *      1. class name *      2. property name *      3. property data type *      4. add/remove new property etc */var peopleModelHashString = hashHelper.HashString(new PeopleModel(){    Id = 1,    Name = "Anders Hejlsberg",    IsActive = true,    CreatedDateTime = new DateTime(1960, 12, 2)});

重要的是记住

此哈希取决于对象结构和分配的值。即使我们为属性分配相同的值,生成的哈希也不会相同,但添加了一些更改,如:

  • /模型名称更改
  • 属性名称更改
  • 命名空间名称更改
  • 属性编号更改(添加或删除任何属性)

到模型。而在开发环境中,重构可能发生的任何时间。

数据值散列

让我们只使用值进行哈希。创建一个接口IHash

public interface IHash{    string HashString();}

使用IHash到模型并在方法HashString()内部使用哈希助手。

class People : IHash{    public long? Id { get; set; }         /*unique identifier, avoid it to use                                             in hash calculation*/    public string Name { get; set; }    public bool? IsActive { get; set; }    public DateTime? CreatedDateTime { get; set; }    public string HashString()    {        var value = new HashHelper().HashString        (Name, IsActive, CreatedDateTime);    /*Add more not constant properties as needed*/        return value;    }}

这样,模型结构不参与哈希生成过程中,只有特定的属性值(NameIsActiveCreatedDateTime)正被考虑。

Hash将保持不变,直到没有为这些属性设置任何新值。模型的任何结构更改(名称更改,属性添加/删除等)都不会影响哈希字符串。

哈希结果

/*constant: 3953fbec5b81ccca72c98655c0c4b069*/people = new People(){    Id = 1,    Name = "Dennis Ritchie",    IsActive = false,    CreatedDateTime = new DateTime(1941, 9, 9)};hashString = people.HashString();

其他测试

使用null 对象值正常工作:

string hashString;/*constant: 47ccecfc14f9ed9eff5de591b8614077*/var people = new People();hashString = people.HashString();

我们将无法创建整个People类,因为它不使用[Serializable]

var hashHelper = new HashHelper(); /*throws error as [Serializable] not been used*/ //var peopleHashString = hashHelper.HashString(people);

奖励:字符串哈希

创建密码/字符串哈希是很常见的。所以我们拥有它。

/*constant: e6fb7af54c39f39507c28a86ad98a1fd*/string name = "Dipon Roy";string value = new HashHelper().HashString(name);

结论

  • 如果我们只需要比较考虑值或特定值,那么使用数据值哈希是最佳选择。
  • 但是如果我们需要完全比较对象结构和值,请选择整个对象的哈希

 

原文地址:

转载地址:http://epzhj.baihongyu.com/

你可能感兴趣的文章
简单的用户头像修改功能(springboot后台)
查看>>
springboot+mybatis实现分页
查看>>
leetcode332. 重新安排行程
查看>>
为什么局域网网段不同不能通信?
查看>>
itchat微信助手,kaggle 电影数据集分析,基于内容的电影推荐
查看>>
认识和使用JWT
查看>>
通过springboot框架,自己动手实现oauth2.0授权码模式认证
查看>>
条件表达式于运算符的点点滴滴的积累
查看>>
最短路径最基本的三种算法【此后无良辰】
查看>>
class的点点滴滴的总结
查看>>
vector 的点点滴滴的总结
查看>>
测试用例
查看>>
自动化测试学习步骤
查看>>
自动化测试需要掌握的知识
查看>>
HTTP协议
查看>>
Python问题总结01
查看>>
Python小程序——冒泡排序
查看>>
cmd中输入net start mysql 提示:服务名无效或者MySQL正在启动 MySQL无法启动
查看>>
LeetCode 206反转链表 [javsScript]
查看>>
[LeetCode javaScript] 3. 无重复字符的最长子串
查看>>