對於我們的下一個初學者項目,我們將建立一個密碼產生器,它不僅可以產生密碼,還可以加密並保存密碼 - 以便它真正發揮作用。
我們將把程式碼分成不同的文件,這樣我們就不會得到一個大的「main.go」文件。
首先,我們初始化一個 go 專案並建立一個「profile.go」文件,其中包含加密和解密密碼的邏輯。
package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/hex" "errors" "io" ) // must be 32 characters var key = "askdjasjdbreonfsdfibsdhfgsdfhboo" var ErrMalformedEncryption = errors.New("malformed encryption") // password in small letters so it is not stored type profile struct { Enc, Platform, password string } func (p *profile) encrypt() error { block, err := aes.NewCipher([]byte(key)) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return err } enc := gcm.Seal(nonce, nonce, []byte(p.password), nil) p.Enc = hex.EncodeToString(enc) return nil } func (p *profile) decrypt() error { block, err := aes.NewCipher([]byte(key)) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } nsize := gcm.NonceSize() if len(p.Enc) <p>這裡我們建立一個包含 3 個欄位的設定檔結構 - Enc、平台和密碼。 Enc 將保存加密的密碼,我們為其產生密碼的服務將儲存在平台中,而密碼將保存實際產生的密碼。設定檔結構有兩種方法「加密」和「解密」。我們使用 AES - 一種對稱金鑰加密演算法來加密和解密我們的密碼。 </p> <p>接下來我們建立一個「store.go」文件,其中包含儲存和檢索密碼的邏輯。 <br> </p> <pre class="brush:php;toolbar:false">package main import ( "encoding/gob" "errors" "os" "sync" ) const filename = "profile.bin" var ( ErrInvalidArgs = errors.New("invalid args") ErrNotFound = errors.New("not found") ) type store struct { sync.RWMutex data map[string]*profile } func newStore() (*store, error) { s := &store{ data: make(map[string]*profile), } if err := s.load(); err != nil { return nil, err } return s, nil } func (s *store) load() error { flags := os.O_CREATE | os.O_RDONLY f, err := os.OpenFile(filename, flags, 0644) if err != nil { return err } defer f.Close() info, err := f.Stat() if err != nil { return err } if info.Size() == 0 { return nil } return gob.NewDecoder(f).Decode(&s.data) } func (s *store) save() error { f, err := os.OpenFile(filename, os.O_WRONLY, 0644) if err != nil { return err } defer f.Close() return gob.NewEncoder(f).Encode(s.data) } func (s *store) find(platform string) (string, error) { s.RLock() defer s.RUnlock() p, ok := s.data[platform] if !ok { return "", ErrNotFound } if err := p.decrypt(); err != nil { return "", err } return p.password, nil } func (s *store) add(platform, password string) error { if platform == "" { return ErrInvalidArgs } p := &profile{ Platform: platform, password: password, } if err := p.encrypt(); err != nil { return err } s.Lock() defer s.Unlock() s.data[platform] = p return s.save() }
我們選擇 gob 檔案進行存儲,因為它們不完全是人類可讀的。如果文件洩露,您的密碼是安全的,因為它們將被加密並且難以讀取。 store 結構包含載入、尋找和儲存到 gob 檔案的方法。我們將密碼保存在字典中。我們也使用互斥體來確保字典並發安全。需要注意的重要一點是,我們不會儲存純生成的密碼 - 我們將儲存其加密值。
現在讓我們來寫幾個實際產生密碼的函數。建立一個「password.go」檔案並輸入以下內容
package main import ( "math" "math/rand" "slices" "strings" ) const ( half = .5 onethird = .3 onefourth = .25 ) var ( randlowers = randFromSeed(lowers()) randuppers = randFromSeed(uppers()) randdigits = randFromSeed(digits()) randsymbols = randFromSeed(symbols()) ) var basicPassword = randlowers func mediumPassword(n int) string { frac := math.Round(float64(n) * half) pwd := basicPassword(n) return pwd[:n-int(frac)] + randuppers(int(frac)) } func hardPassword(n int) string { pwd := mediumPassword(n) frac := math.Round(float64(n) * onethird) return pwd[:n-int(frac)] + randdigits(int(frac)) } func xhardPassword(n int) string { pwd := hardPassword(n) frac := math.Round(float64(n) * onefourth) return pwd[:n-int(frac)] + randsymbols(int(frac)) } func randFromSeed(seed string) func(int) string { return func(n int) string { var b strings.Builder for range n { b.WriteByte(seed[rand.Intn(len(seed))]) } return b.String() } } func lowers() string { var b strings.Builder for i := 'a'; i <p>在這裡,我們編寫了產生不同難度等級密碼的函數。 basicPassword 函數產生隨機小字母字串。 mediumPassword 函數從 basicPassword 函數中獲取一小部分字符,並在其中添加隨機大寫字母。 HardPassword 函數對mediumPassword 執行相同的操作,但增加了數字。 xhardPassword 執行相同操作並新增符號。 shuffle 函數完全符合您對切片的期望,而 shuffleStr 則對字串進行洗牌。 </p> <p>現在讓我們把所有東西放在一起。建立一個「main.go」檔案並輸入以下內容<br> </p> <pre class="brush:php;toolbar:false">package main import ( "errors" "flag" "fmt" "log" "regexp" "strconv" "strings" ) var usage = ` Usage ----- --get platform=[string] - Gets saved password for a platform --set platform=[string] len=[int] level=(basic|medium|hard|xhard) - Creates and saves a password ` var ErrUsage = errors.New(usage) var pattern = regexp.MustCompile(`\S+=\S+`) type level int const ( _ level = iota level_basic level_medium level_hard level_xhard ) var level_key = map[string]level{ "basic": level_basic, "medium": level_medium, "hard": level_hard, "xhard": level_xhard, } type commands struct { get, set bool } func createCommands() (c commands) { flag.BoolVar(&c.get, "get", false, "get password for platform") flag.BoolVar(&c.set, "set", false, "set password for platform") flag.Parse() return } func (c commands) exec(store *store) (string, error) { switch { case c.get: return c.getPassword(store) case c.set: return c.setPassword(store) default: return "", ErrUsage } } func (c commands) getPassword(store *store) (string, error) { params, err := c.parse() if err != nil { return "", err } return store.find(params["platform"]) } func (c commands) setPassword(store *store) (string, error) { params, err := c.parse() if err != nil { return "", err } var password string n, err := strconv.Atoi(params["len"]) if err != nil { return "", err } if n <p>我們使用標誌來指定我們期望應用程式的行為方式。 「--get」取得密碼,「--set」產生並儲存密碼。若要設定密碼,使用者提供帶有標誌的參數,以指示應用程式產生和儲存的密碼類型。要取得密碼,使用者還提供參數來指定要檢索的密碼。 </p> <p>您現在可以執行「go build」來建立二進位檔案並測試應用程式。 </p> <p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173158638678335.jpg?x-oss-process=image/resize,p_40" class="lazy" alt="Let"></p>
以上是讓我們建立一個我們可以實際使用的密碼產生器的詳細內容。更多資訊請關注PHP中文網其他相關文章!