C#-用Winform製作一個簡單的密碼管理工具

聞風聽雨發表於2020-08-09

為什麼要做?

首先是為了練習一下c#。
想必大家都有過記不起某個平臺的賬號密碼的經歷,那種感受著實令人抓狂。那這麼多賬號密碼根本記不住!我之前用python寫過一個超級簡單(連賬號資訊都寫在程式碼裡那種)的控制檯程式用來給我提示密碼,但是我想新增一個賬號時直接被麻煩到吐。 所以我才想用Winform做一個簡單的小工具來幫助我記憶。(僅供我自己使用(所以介面會比較醜。。),但是我會把程式碼貼出來所以有點c#基礎的其實都可以自己做一個)

設想

我的需求非常簡單

  1. 在我需要的時候,輸入一個平臺能把對應的賬號密碼顯示出來
  2. 能夠新增賬號資訊

還有一點就是在查詢賬號之前需要輸入一個口令來驗證身份,這個口令只有我自己知道(我把它存在了app.config檔案中,後續如果有需要可以擴充套件出更改口令的功能),所以即使別人用我的電腦執行起來這個程式,他不知道口令也是沒用的。

賬號資訊怎麼存?

我曾經想要用SQL Server存,畢竟c#與它如此親近,但是我要存的東西本質上只是一些字串,感覺有點大材小用所以沒有選擇SQL Server。最近學了JS瞭解了一些json的知識發現json檔案是個很好的選擇於是我便決用它了。但是我還不會用c#處理json資料呀。於是我又去網上找方法,然後我就發現了newtonsoft.json(json.NET) ,它時一款.NET中開源的json序列化與反序列化工具。有了它,就可以解決我的問題了。

code

首先把入口介面搭建起來,一個超級簡單的窗體,只放了三個控制元件。

入口

為了方便操作給它加一個退出的快捷鍵ESC。非常簡單隻需要在KeyDown事件中寫下如下程式碼:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if(e.KeyCode==Keys.Escape)
    {
        this.Close();
    }
}

然後是確定這個按鈕的功能:當輸入正確的口令後,點選確定可以進入到下一個介面。

口令放在哪?

我將口令這個資料放在了App.config檔案中,在解決方案資源管理器中右鍵新增新建項就可以新增它了。然後向其中加入如下程式碼:

  <appSettings>
    <add key="CMD" value="123123"/>
  </appSettings>

然後給剛才的入口窗體新增一個欄位CMD並用ConfigurationManager將剛才的配置讀取出來賦值給它:

public readonly string CMD=ConfigurationManager.AppSettings["CMD"].ToString() ;

上面的確定按鈕的功能是口令正確是將下個介面顯示出來,所以我們先把下個介面建立出來:

有了這個介面,就可以去寫第一個介面中確定按鈕的點選事件了:

private void button1_Click(object sender, EventArgs e)
{
    ;
    string entered_cmd = textBox1.Text; 
    if(entered_cmd.Equals(CMD))
    {
        (new GetPwd()).Show();
        this.Hide();
    }
    else
    {
        MessageBox.Show("錯誤!","警告",MessageBoxButtons.OK, MessageBoxIcon.Warning);
    }
}

這樣第一個介面的程式碼就寫完了(沒錯就是這麼簡單)。

賬號資訊查詢介面

也就是上面的第二個窗體。它載入的初始狀態是這樣:

用於顯示賬號和密碼的兩個文字框我設定成了不可見(當輸入的平臺是存在的並點選確定就可以看到了)和只讀。像這樣:

一個小問題

和上一個窗體一樣,我也給這個窗體繫結了enter(相當於點選確定按鈕)和esc快捷鍵。但是當我關閉這個窗體時,已經無法再進行任何操作了可是這個程式程式卻沒有結束。怎麼解決這個問題呢?前段時間學習了委託於是我想到了一個比學校老師曾經教過的更好的方法(當時還沒學習委託,老師教的是把第一個窗體物件傳遞給第二個窗體):給第二個窗體定義一個委託欄位 public Action close_main;然後在顯示該窗體是將前一個窗體的close()方法傳遞過來即可。
前面的程式碼修改一下:

(new GetPwd() {close_main=this.Close}).Show();

然後在第二個窗體的FormClosed事件中執行這個方法就可以了。

private void GetPwd_FormClosed(object sender, FormClosedEventArgs e)
{
    close_main();
}

查詢

所有的賬號資訊我都放在一個json檔案中:PWD.json,有關對其的各種邏輯性操作我抽成了一個類Manager:

class Manager
{
    public static string GetjsonString()
    {
        // 獲取整個json字串
        if(!File.Exists(Path.GetFullPath(@"..//..") + ".../PWD.JSON"))
        {
            File.Create(Path.GetFullPath(@"..//..") + ".../PWD.JSON");
        }
        StreamReader jsonFile = File.OpenText(Path.GetFullPath(@"..//..") + ".../PWD.JSON");
        string res = jsonFile.ReadToEnd();
        jsonFile.Close();
        return res;
    }
    public static List<Acount> GetAcountsList()
    {
        //將json字串序列化為一個集合物件
        return JsonConvert.DeserializeObject<List<Acount>>(GetjsonString());
    }
    public static Acount SearchAcount(string platform)
    {
        //搜尋平臺為platform的賬號
        List<Acount> acounts = GetAcountsList();
        if(acounts==null||acounts.Count==0)
        {
            return null;
        }
        var res = from a in acounts
              where a.platForm == platform
              select a;
        return res.Count()>=1?res.First():null;
    }
    public static bool CheckExistence(Acount acount)
    {
        //檢查acount 這個賬號是否存在
        //檢查該賬號是否存在
        List<Acount> acounts = GetAcountsList();
        var res = from a in acounts
                  where a.platForm==acount.platForm&&a.acount == acount.acount && a.password == acount.password
                  select a;
        return res.Count() == 1;
    }
    public static void AddAcount(Acount a)
    {
        //新增一個賬號資訊
        if(a.platForm==""||a.acount==""||a.password=="")
        {
            MessageBox.Show("請輸入完整資訊!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return ;
        }
        List<Acount> acounts = GetAcountsList();
        acounts.Add(a);
        string res = JsonConvert.SerializeObject(acounts);
        StreamWriter jsonwriter = new StreamWriter(Path.GetFullPath(@"..//..") + ".../PWD.JSON");
        jsonwriter.WriteLine(res);
        jsonwriter.Close();
    }
}

有了這些方法,其他地方就非常簡單了。查詢介面的確定按鈕點選事件:

private void button1_Click(object sender, EventArgs e)
{
    Acount acount = Manager.SearchAcount(txt_platform.Text.ToLower());
    if(acount!=null)
    {
        txt_uId.Text = acount.acount;
        txt_pwd.Text = acount.password;
        txt_uId.Visible = true;
        txt_pwd.Visible = true;
        txt_uId.ReadOnly = true;
        txt_pwd.ReadOnly = true;
    }
    else
    {
        MessageBox.Show("無此賬號!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    }
}

新增

第三個介面用來新增,通過第二個介面的Add按鈕撥出。
長這樣:

我的設想是想要新增賬號資訊需要再次驗證一下身份:輸入一個已有的正確賬號資訊來通過驗證,驗證通過後仍然使用這個介面來新增(三個文字框依次用來輸入平臺、賬號、密碼)。這個介面只有一個按鈕,驗證時它的text屬性為"Check",新增時為"Add",窗體的標題也有相應的變化。該按鈕的點選事件如下:

private void button1_Click(object sender, EventArgs e)
{
    if(btn_check.Text=="Check")
    {
        if (Manager.CheckExistence(new Acount(txt_check_platform.Text.ToLower(), txt_check_uid.Text, txt_check_pwd.Text)))
        {
            txt_check_platform.Text = "";
            txt_check_uid.Text = "";
            txt_check_pwd.Text = "";
            this.Text = "新增密碼";
            btn_check.Text = "Add";
            txt_check_platform.Focus();
        }
        else
        {
            MessageBox.Show("驗證失敗!!", "驗證反饋", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
    }
    else
    {
        Manager.AddAcount(new Acount(txt_check_platform.Text.ToLower(), txt_check_uid.Text, txt_check_pwd.Text));
        this.close();
    }            
}

新增時:

另外,第三個介面也做了和前一個介面相同的快捷鍵和關閉處理

最後

這個程式非常非常簡單,當然後續還是可以擴充套件許多其他功能的。雖然製作過程很簡單但是我還是有很重要的收穫,就是newtonsoft.json這個工具。當然還有一點時減輕了我對無數密碼的記憶負擔。

以記錄自己的學習歷程

相關文章