使用它作為短連結生成可以嗎?- Hashids.net

使用它作為短連結生成可以嗎?- Hashids.net

hashids.net是一款輕量級開源的將數字編碼成字符串的加密(短id生成)工具類庫,其實靈活用它將字符串生成短id也是可以的,只是不可逆。

最后更新 2023/9/17 下午8:31
Rector
预计阅读 10 分钟
分类
.NET
标签
.NET C# 開源

概述

本文為.net 開發者們分享一款輕量級開源的將數字編碼成字符串的加密(短 id 生成)工具類庫-hashids.net

無論在前端還是後端的編程開發中,都會遇到讓系統自動生成一些編碼或者 id 的場景,並且要求生成的編碼或 id 是不重複的(重複率極低的)。

前端开发中,常用的有nanoid。而后端开发中,常用的技术则有:自增 ID,雪花 ID,GUID 等。其中,自增 ID 在中小型系统中使用比较常见,它占用的存储空间相对较小,检索速度相对较快,但它不适用于分布式系统的构建,而雪花 ID 和 GUID 等占用字节较多,占用存储空间较大,检索速度相对较慢,但后两者适用于分布式的系统构建。

另外,还有一些场景,为了隐藏后端的真实 ID,在显示到客户端时,对真实 ID 进行加密处理,将真实的数字加密生成一个短的字符串,比如国外知名视频网站油管的视频地址类似https://www.yt.com/watch?v=yVd7vbeFj-g,其中的参数v的值yVd7vbeFj-g即为一个加密的字符串。

在.net, .net core, .net 5\6\7\8 等程式開發中,如果你也想生成類似的加密字符串,本文為.net 開發者們推薦hashids.net這個開源的短 id 生成(加密)類庫。

Hashids.net 功能和特性

Hashids.net可以将数字转换成字符串,比如将347转换成yr8,或者将数字数组[27, 986]转换成3kTMd。当然,你也可以将转换后的字符串再次转换成数字或者数字数组。这在将多个参数捆绑成一个参数、隐藏实际 ID 或简单地将它们用作短字符串 ID 时非常有用。

Hashids.net 主要有如下的特性:

  • 將整數轉換成惟一的短 id(僅支持包含零在內的正整數)
  • 為自增 id 生成不可推測的非連續 id
  • 支持單個數字或數字數組
  • 允許自定義字母和鹽
  • 允許指定最小哈希長度

Hashids.net 的安裝

Hashid.net 以 nuget 包發布,所以有如下的安裝方式:

1.nuget 命令行

Install-Package Hashids.net

2.nuget 程式包管理工具

在項目中右鍵單擊依賴項,如圖:

然后,在打开的 NuGet 程序包管理界面输入关键字Hashids.net,在搜索到的结果中选中Hashids.net类库组件并安装,如图:

Hashids.net 的使用

導入Hashids.net的命名空間

using HashidsNet;

編碼單個數字

实例化Hashids对象时,你可以传递一个唯一的盐值,这样你的哈希值就不同于其他人的哈希值。这里使用this is my salt作为例子。

var Hashids = new Hashids("this is my salt");
var hash = hashids.Encode(12345);

运行结果为:NkK9

如果要转换一个Int64类型的数字,则需要调用EncodeLong()方法,如下:

var hashids = new Hashids("this is my salt");
var hash = hashids.EncodeLong(666555444333222L);

运行结果为:KVO9yy1oO5j

解碼

hashids.net提供了將已編碼的字符串反解碼的功能,但解碼時需使用與編碼相同的鹽值:

var hashids = new Hashids("this is my salt");
numbers = hashids.Decode("NkK9");

运行结果为:[ 12345 ]

var hashids = new Hashids("this is my salt");
numbers = hashids.DecodeLong("KVO9yy1oO5j");

运行结果为:[ 666555444333222L ]

使用不同的鹽值解碼

如果解碼時的鹽值與編碼時不相同,則解碼將失敗:

var hashids = new Hashids("this is my pepper");
numbers = hashids.Decode("NkK9");

运行结果为:[]

編碼多個數字

var hashids = new Hashids("this is my salt");
var hash = hashids.Encode(683, 94108, 123, 5);

运行结果为:aBMswoO2UB3Sj

多個數字編碼後的解碼

var hashids = new Hashids("this is my salt");
var numbers = hashids.Decode("aBMswoO2UB3Sj")

运行结果为:[ 683, 94108, 123, 5 ]

指定編碼後的最小哈希長度

以下示例將對整數 1 進行編碼,並將最小哈希長度設置為 8(默認情況下是 0--這意味著哈希將是可能的最短長度)。

var hashids = new Hashids("this is my salt", 8);
var hash = hashids.Encode(1);

运行结果为:gB0NV05e

解碼

var hashids = new Hashids("this is my salt", 8);
var numbers = hashids.Decode("gB0NV05e");

运行结果为:[ 1 ]

自定義哈希字母

以下示例指定了自定义的哈希字母为abcdefghijkABCDEFGHIJK12345

var hashids = new Hashids("this is my salt", 0, "abcdefghijkABCDEFGHIJK12345")
var hash = hashids.Encode(1, 2, 3, 4, 5)

运行结果为:Ec4iEHeF3

Hashids.net 的隨機性

Hashids.net的主要目的是混淆 ID,此外,它还可以让有规律的数字变得不可猜测和不可预测。

編碼重複的數字

var hashids = new Hashids("this is my salt");
var hash = hashids.Encode(5, 5, 5, 5);

编码后,你不会看到任何重复的模式来表明哈希中有 4 个相同的数字,运行结果为:1Wc8cwcE

編碼有序的數字

var hashids = new Hashids("this is my salt");
var hash = hashids.Encode(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

运行结果为:kRHnurhptKcjIDTWC3sx

編碼自增的數字

var hashids = new Hashids("this is my salt");

hashids.Encode(1); // => NV
hashids.Encode(2); // => 6m
hashids.Encode(3); // => yD
hashids.Encode(4); // => 2l
hashids.Encode(5); // => rD

編碼十六進位

var hashids = new Hashids("this is my salt");
var hash = hashids.EncodeHex("DEADBEEF");

运行结果为:kRNrpKlJ

解碼十六進位

var hashids = new Hashids("this is my salt");
var hex = hashids.DecodeHex("kRNrpKlJ");

运行结果为:DEADBEEF

將字符串加密生成短字符串怎樣?

以下為站長擴展長字符串轉短字符串方法居間

其實很簡單,將需要加密的原字符串作為鹽,加密數字固定,這樣就簡單實現了:

public static string GetHashids(this string sourceStr, int number = 9)
{
    var hashids = new Hashids(sourceStr);
    return hashids.Encode(number);
}

單元測試如下:比如將本文別名【is-it-possible-to-use-it-as-a-short-link-generator-hashidsnet】加密

[TestClass]
public class HashHelperUnitTest
{
    [TestMethod]
    public void Hashids_Success()
    {
        var blogPostSlugStr = "Is-it-possible-to-use-it-as-a-short-link-generator-Hashidsnet";

        var encodeStr1 = blogPostSlugStr.GetHashids();
        var encodeStr2 = blogPostSlugStr.GetHashids();

        Assert.AreEqual(encodeStr1, encodeStr2);
    }
}

別名加密後為:【6q】,可打開瀏覽器訪問本文短連結地址嘗試:https://dotnet9.com/6Q。

註:這樣使用不是本庫的初衷,但將長字符串轉成短字符串這確不失為一種好想法,哈哈,記住這不可逆哈。

小知識

將一個長字符串轉成短字符串有多種方法可實現,常規做法:

  1. 使用資料庫存儲短字符串和原字符串,短字符串可自行定義規則,比如從 1 開始取值,每次取值時讀資料庫,根據短字符串取長字符串或根據長字符串取短字符串。

優點:短字符串與原字符串一一對應,可以實現雙向轉換,不會出現重複的短字符串。

缺點:需要頻繁讀寫資料庫,對資料庫的性能有一定的影響。

  1. 將長字符串使用一種算法轉成短字符串,有算法是可逆的,有的是不可逆的。

無需資料庫存儲,但長度可能會略長。

  • 可逆算法示例(c#):
public static string ShortenString(string longString)
{
    byte[] bytes = Encoding.UTF8.GetBytes(longString);
    string shortString = Convert.ToBase64String(bytes);
    return shortString;
}

public static string RestoreString(string shortString)
{
    byte[] bytes = Convert.FromBase64String(shortString);
    string longString = Encoding.UTF8.GetString(bytes);
    return longString;
}
  • 不可逆算法示例(c#):
public static string ShortenString(string longString)
{
    using (SHA256 sha256 = SHA256.Create())
    {
        byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(longString));
        string shortString = Convert.ToBase64String(hashBytes);
        return shortString;
    }
}
  • 區別:
    • 可逆算法可以通過短字符串還原為原字符串,而不可逆算法無法還原。
    • 可逆算法生成的短字符串長度較長,而不可逆算法生成的短字符串長度較短。
    • 使用算法自動生成短字符串無需頻繁讀寫資料庫,性能較好,但可能存在短字符串衝突的問題,即不同的長字符串可能生成相同的短字符串。

最後列出完整的單元測試希望對您有用:

using HashidsNet;
using System.Security.Cryptography;
using System.Text;

namespace Dotnet9.Commons.Test;

[TestClass]
public class HashHelperUnitTest
{
    public static string GetHashids(string sourceStr, int number = 9)
    {
        var hashids = new Hashids(sourceStr);
        return hashids.Encode(number);
    }

    [TestMethod]
    public void Hashids_Success()
    {
        var blogPostSlugStr = "Is-it-possible-to-use-it-as-a-short-link-generator-Hashidsnet";

        var encodeStr1 = GetHashids(blogPostSlugStr);
        var encodeStr2 = GetHashids(blogPostSlugStr);

        Assert.AreEqual(encodeStr1, encodeStr2);
    }


    [TestMethod]
    public void Hashids_Best_Success()
    {
        var blogPostSlugStr = "Is-it-possible-to-use-it-as-a-short-link-generator-Hashidsnet";

        var encodeStr1 = blogPostSlugStr.GetHashids();
        var encodeStr2 = ShortenString(blogPostSlugStr);
        var encodeStr3 = ShortenString2(blogPostSlugStr);

        Assert.IsTrue(encodeStr1.Length < encodeStr2.Length, "Hashids生成的短字符串比Base64还短");

        Assert.IsTrue(encodeStr1.Length < encodeStr3.Length, "Hashids生成的短字符串还是短那么一点点");
    }

    public static string ShortenString(string longString)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(longString);
        string shortString = Convert.ToBase64String(bytes);
        return shortString;
    }

    public static string RestoreString(string shortString)
    {
        byte[] bytes = Convert.FromBase64String(shortString);
        string longString = Encoding.UTF8.GetString(bytes);
        return longString;
    }

    public static string ShortenString2(string longString)
    {
        using (SHA256 sha256 = SHA256.Create())
        {
            byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(longString));
            string shortString = Convert.ToBase64String(hashBytes);
            return shortString;
        }
    }
}

本文大部分引用知乎原文章:

原文標題:分享.net/.net 5 輕量級開源的將數字編碼成字符串的加密(短 id 生成)工具類庫--hashids.net

原文連結:https://codedefault.com/p/lightweight-open-source-project-hashids-net-for-generating-short-ids-from-numbers

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2026/2/7

aot使用經驗總結

從項目創建伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 aot 發布測試。

继续阅读