日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core 仿魔兽世界密保卡实现

發布時間:2023/12/4 asp.net 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core 仿魔兽世界密保卡实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

《魔獸世界》的老玩家都知道,密保卡曾經被用于登錄驗證,以保證賬號安全。今天我用.NET Core模擬了一把密保卡(也叫矩陣卡)的實現,分享給大家。

密保卡的原理

這是一張典型的魔獸世界密保卡。序列號用于綁定游戲賬號,而下面表格中的數字用于登錄驗證。

(圖片來源于網絡)

假設黑客已經知道了你的賬號和密碼,但是由于你綁定了一張密保卡。因此在登錄游戲時,游戲會隨機挑選其中一定數量(一般是3)個格子,要求輸入對應的數字,如A1=928,C8=985,B10=640。而因為黑客沒有拿到你的密保卡,因此他不知道矩陣中的數字,無法登錄你的賬號。即使抓取了幾次你的輸入,但由于每次登錄賬號被隨機選中的單元格組合都不同,因此對于一張7X12的密保卡,黑客需要抓(對不起我數學40分這個算不出來)次,才能完全掌握你的密保卡信息。然而賬號主人可以隨時更換密保卡,讓黑客前功盡棄。

.NET Core 實現

關注我博客的朋友可能知道,8年前我寫過這個話題,兩篇文章分別是:《C#仿魔獸世界密保卡簡單實現》與《C#仿魔獸世界密保卡OOP重構版》。

但是時代變了,獸人永不為奴,而.NET必將為王。8年了,當年文章里用的ASP.NET WebForm和巫妖王一起死在了冰封王座,.NET踏上了跨平臺的遠征,C# 的語法也突飛猛進的發展。榮耀屬于.NET Core,因此我把這盤冷飯拿出來炒一下,用現代化的手段重寫當年的老代碼,刷刷聲望。

最終效果如下,實現生成、序列號數據、重新加載數據以及驗證輸入:

源代碼傳送門:https://go.edi.wang/fw/5d12778d

Cell 類

Cell用于描述矩陣卡中的單元格。對于一個Cell,它擁有行標列標三個屬性。我分別用RowIndexColIndexValue來表示。為了方便顯示,我加入了ColumnName屬性,用于把列標顯示為英文字母(此處稍微和官方密保卡設計不一樣)。

為了約束Cell類型的使用,以上屬性設計為只讀,并只能從構造函數賦值。

public class Cell

{

? ? public int RowIndex { get; }

? ? public int ColIndex { get; }

? ? public ColumnCode ColumnName => (ColumnCode)ColIndex;

? ? public int Value { get; set; }


? ? public Cell(int rowIndex, int colIndex, int val = 0)

? ? {

? ? ? ? RowIndex = rowIndex;

? ? ? ? ColIndex = colIndex;

? ? ? ? Value = val;

? ? }

}

public enum ColumnCode

{

? ? A = 0,

? ? B = 1,

? ? C = 2,

? ? D = 3,

? ? E = 4

}

ColumnCode 可以根據自己需要拓展,目前我只寫了5個值。

Card 類

Card用于描述一張密保卡。因此除了包含一堆Cell以外,還得有卡號(Id),以及行數、列數等信息。起初的Card類型長這樣:

public class Card

{

? ? public Guid Id { get; set; }

? ? public int Rows { get; set; }

? ? public int Cols { get; set; }

? ? public List<Cell> Cells { get; set; }


? ? public Card(int rows = 5, int cols = 5)

? ? {

? ? ? ? Id = Guid.NewGuid();


? ? ? ? Rows = rows;

? ? ? ? Cols = cols;

? ? ? ? Cells = new List<Cell>();

? ? }

}

但是考慮到序列化數據時候不希望字符串有太多冗余信息,因此加入CellData屬性用于簡化Cells的數據表示。將Cells中的數據拼成一個以逗號分隔的字符串中。以便于持久化的時候和Card類型的屬性一起包在一個Json字符串中,看起來不會太長。

[JsonIgnore]

public List<Cell> Cells { get; set; }


public string CellData

{

? ? get

? ? {

? ? ? ? var vals = Cells.Select(c => c.Value);

? ? ? ? return string.Join(',', vals);

? ? }

}

生成密保卡數據

首先,根據行、列數量,生成一個二位數組,使用0-100的隨機值填充。值范圍可以根據自己需要改。

private static int[,] GenerateRandomMatrix(int rows, int cols)

{

? ? var r = new Random();

? ? var arr = new int[rows, cols];

? ? for (var row = 0; row < rows; row++)

? ? {

? ? ? ? for (var col = 0; col < cols; col++)

? ? ? ? {

? ? ? ? ? ? arr[row, col] = r.Next(0, 100);

? ? ? ? }

? ? }

? ? return arr;

}

然后將生成的值按行、列分配給Cells屬性

private void FillCellData(int[,] array)

{

? ? for (var row = 0; row < Rows; row++)

? ? {

? ? ? ? for (var col = 0; col < Cols; col++)

? ? ? ? {

? ? ? ? ? ? var c = new Cell(row, col, array[row, col]);

? ? ? ? ? ? Cells.Add(c);

? ? ? ? }

? ? }

}

在Console上打印密保卡信息也很簡單,用兩個循環分別控制行、列的輸出即可。(當然,這只是demo意圖,真實使用場景用不著console)

private static void PrintCard(Card card)

{

? ? Console.WriteLine("? |\tA\tB\tC\tD\tE\t");

? ? Console.WriteLine("----------------------------------------------");

? ? var i = 0;

? ? for (var k = 0; k < card.Rows; k++)

? ? {

? ? ? ? Console.Write(k + " |\t");

? ? ? ? for (var l = 0; l < card.Cols; l++)

? ? ? ? {

? ? ? ? ? ? Console.Write(card.Cells[i].Value + "\t");

? ? ? ? ? ? i++;

? ? ? ? }

? ? ? ? Console.WriteLine();

? ? }

}

加載Cells數據

除了生成數據,我們還要支持加載既有數據到Cells中。

因為之前被簡化過的Cells數據是個以逗號分割的string字符串,因此我們需要把它拆成數組,并轉換類型回int,然后利用之前寫的FillCellData()方法填充到Cells屬性里。

public Card LoadCellData(string strMatrix)

{

? ? var tempArrStr = strMatrix.Split(',');

? ? if (tempArrStr.Length != Rows * Cols)

? ? {

? ? ? ? throw new ArgumentException(

? ? ? ? ? ? "The number of elements in the matrix does not match the current card cell numbers.", nameof(strMatrix));

? ? }


? ? var arr = new int[Rows, Cols];


? ? var index = 0;

? ? for (var row = 0; row < Rows; row++)

? ? {

? ? ? ? for (var col = 0; col < Cols; col++)

? ? ? ? {

? ? ? ? ? ? arr[row, col] = int.Parse(tempArrStr[index]);

? ? ? ? ? ? index++;

? ? ? ? }

? ? }


? ? FillCellData(arr);

? ? return this;

}

隨機選擇與驗證

同樣使用Random類型,在給定的行列范圍內隨機選擇給定數量的單元格,但不從Cells中取,因為我們無需返回單元格的值。在服務器/客戶端場景下,驗證始終應該放在服務器上做,不要在客戶端驗證值,因此不要返回值。

public IEnumerable<Cell> PickRandomCells(int howMany)

{

? ? var r = new Random();

? ? for (var i = 0; i < howMany; i++)

? ? {

? ? ? ? var randomCol = r.Next(0, Cols);

? ? ? ? var randomRow = r.Next(0, Rows);

? ? ? ? var c = new Cell(randomRow, randomCol);

? ? ? ? yield return c;

? ? }

}

由于返回的Cell信息包含了行、列,因此當用戶輸入值之后,我們可以與Cells中已存在的信息進行對比。

對于每一個需要驗證的單元格:

  • 在Cells中查找具有同樣行列的單元格。

  • 對比這兩者的值是否相等,一旦遇到不相等直接返回false,無需再驗證下一個單元格。

  • 通常這樣的操作某些語言就得寫好幾個循環,不僅麻煩,還容易下標搞錯數組越界然后996。好在C#的LINQ一行就寫完了:(換行只是代碼格式)

    public bool Validate(IEnumerable<Cell> cellsToValidate)

    {

    ? ? return (

    ? ? ? ? from cell in cellsToValidate

    ? ? ? ? let thisCell = Cells.Find(p => p.ColIndex == cell.ColIndex

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&& p.RowIndex == cell.RowIndex)

    ? ? ? ? select thisCell.Value == cell.Value)

    ? ? ? ? .All(matches => matches);

    }

    完整代碼傳送門:https://go.edi.wang/fw/5d12778d

    總結

    以上是生活随笔為你收集整理的.NET Core 仿魔兽世界密保卡实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。