[TOC]
# 簡介
最近正在遷移自己的小項目,項目之前是基于**Laravel5.5**開發的。整個用戶登陸也是基于框架的 Auth 包認證的。其中用戶密碼這塊也是用到了`PHP`內置的函數`password_hash`,用它進行密碼加密。而且 PHP 默認使用的 PASSWORD\_BCRYPT 算法。在使用 Go 的遷移過程中需要認證密碼,所以就把這個過程記錄下來。使用下面的例子來說明如何使用 GO[bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt)包來對你的密碼進行 hash 和 salt 加密
對于這個例子,我將創建一個控制臺應用程序,用于演示如何獲取用戶輸入的密碼并使用它生成 salt 哈希值。 完成此操作后,我將通過比較密碼與其散列版本來驗證密碼是否正確。
# 獲取用戶輸入的密碼
開始我們先創建一個可以在控制臺讀取用戶輸入的的方法。
~~~go
func getPwd() []byte {
fmt.Println("Enter a password")
var pwd string
// 讀取用戶輸入
_, err := fmt.Scan(&pwd)
if err != nil {
log.Println(err)
}
return []byte(pwd)
}
~~~
# Hash & Salt 用戶的密碼
現在我們可以使用 Go 的?[bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt)?包提供的`GenerateFromPassword(password []byte, cost int)([]byte, error)`方法對用戶的密碼進行 hash 和 salt 加密了。
> GenerateFromPassword 方法以給定 cost 值返回密碼的 Bcrypt 算法的 Hash 值,如果提供的 cost 值小于 Mincost 的話,將會默認使用 DefaultCost 代替
>
> 使用`GenerateFromPassword`函數的一個優勢就是我們不需要自己來編寫函數來生成 Salt,因為它會為我們自動生成一個 Salt。
下面的函數使用`GenerateFromPassword`生成 salted 哈希值,該哈希值作為字節切片返回。 然后我們將字節切片作為字符串返回,以便我們可以將 salted 哈希存儲在數據庫中作為用戶密碼。
~~~go
func hashAndSalt(pwd []byte) string {
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
if err != nil {
log.Println(err)
}
return string(hash)
}
~~~
# 目前我們做了什么
到目前為止,我們已經創建了一個接受來自控制臺的用戶輸入并將其作為字節切片返回的函數。 然后,
我們再創建一個可以接收用戶輸入并返回 salted 哈希值的函數。下面就是代碼事例。
~~~go
package main
import (
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
)
func main() {
for {
pwd := getPwd()
hash := hashAndSalt(pwd)
fmt.Println("Salted Hash", pwd)
}
}
~~~
如果你運行上面的代碼,將會得到下面的結果
~~~go
> $ Enter a password
> $ foobar
> Salted Hash $2a$10$...........
~~~
這里需要的注意的是我使用 for 循環調用函數,直到我強制停止它。對于那些不熟悉 GO 的人來講,這個就和其他語言的`while (true){}`是一樣的效果。
# 驗證密碼
最后一件事兒就是需要驗證密碼的正確性來登陸我們的系統,我們可以使用[bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt)?包提供的`CompareHashAndPassword(hashedPassword, password []byte) error`函數
> CompareHashAndPassword 將 bcrypt 哈希密碼與其純文本進行比較。 成功時返回 nil,失敗時返回錯誤
>
> 我們使用`CompareHashAndPassword`函數來創建另一個返回 bool 值的函數讓我們知道密碼是否匹配。
~~~go
func comparePasswords(hashedPwd string, plainPwd []byte) bool {
byteHash := []byte(hashedPwd)
err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)\
if err != nil {
log.Println(err)
return false
}
return true
}
~~~
# 更新 Main 函數
我們現在可以更新我們的主要功能,以便我們能夠輸入密碼,獲取其鹽漬哈希,然后再次輸入密碼,并查明我們的第二個密碼是否與我們輸入的第一個密碼相匹配。
我們現在修改一個 main 函數,當我們輸入密碼的時候,獲取 salted 哈希值,然后再次輸入密碼,來檢查我們的密碼是否匹配。
~~~go
func main() {
for {
pwd := getPwd()
hash := hashAndSalt(pwd)
pwd2 := getPwd()
pwdMatch := comparePasswords(hash, pwd2)
fmt.Println("Passwords Match?", pwd)
}
}
~~~
# 全部代碼
~~~go
package main
import (
"fmt"
"log"
"golang.org/x/crypto/bcrypt"
)
func main() {
for {
// 輸入密碼 獲取 hash 值
pwd := getPwd()
hash := hashAndSalt(pwd)
// 再次輸入密碼驗證
pwd2 := getPwd()
pwdMatch := comparePasswords(hash, pwd2)
fmt.Println("Passwords Match?", pwd)
}
}
func getPwd() []byte {
fmt.Println("Enter a password")
var pwd string
_, err := fmt.Scan(&pwd)
if err != nil {
log.Println(err)
}
return []byte(pwd)
}
func hashAndSalt(pwd []byte) string {
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
if err != nil {
log.Println(err)
}
return string(hash)
}
func comparePasswords(hashedPwd string, plainPwd []byte) bool {
byteHash := []byte(hashedPwd)
err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)
if err != nil {
log.Println(err)
return false
}
return true
}
~~~
以上便是 GO 轉 php 的加密函數的過程,如果有任何錯誤或者不當的地方歡迎進行改進
- 基礎
- 簡介
- 主要特征
- 變量和常量
- 編碼轉換
- 數組
- byte與rune
- big
- sort接口
- 和mysql類型對應
- 函數
- 閉包
- 工作區
- 復合類型
- 指針
- 切片
- map
- 結構體
- sync.Map
- 隨機數
- 面向對象
- 匿名組合
- 方法
- 接口
- 權限
- 類型查詢
- 異常處理
- error
- panic
- recover
- 自定義錯誤
- 字符串處理
- 正則表達式
- json
- 文件操作
- os
- 文件讀寫
- 目錄
- bufio
- ioutil
- gob
- 棧幀的內存布局
- shell
- 時間處理
- time詳情
- time使用
- new和make的區別
- container
- list
- heap
- ring
- 測試
- 單元測試
- Mock依賴
- delve
- 命令
- TestMain
- path和filepath包
- log日志
- 反射
- 詳解
- plugin包
- 信號
- goto
- 協程
- 簡介
- 創建
- 協程退出
- runtime
- channel
- select
- 死鎖
- 互斥鎖
- 讀寫鎖
- 條件變量
- 嵌套
- 計算單個協程占用內存
- 執行規則
- 原子操作
- WaitGroup
- 定時器
- 對象池
- sync.once
- 網絡編程
- 分層模型
- socket
- tcp
- udp
- 服務端
- 客戶端
- 并發服務器
- Http
- 簡介
- http服務器
- http客戶端
- 爬蟲
- 平滑重啟
- context
- httptest
- 優雅中止
- web服務平滑重啟
- beego
- 安裝
- 路由器
- orm
- 單表增刪改查
- 多級表
- orm使用
- 高級查詢
- 關系查詢
- SQL查詢
- 元數據二次定義
- 控制器
- 參數解析
- 過濾器
- 數據輸出
- 表單數據驗證
- 錯誤處理
- 日志
- 模塊
- cache
- task
- 調試模塊
- config
- 部署
- 一些包
- gjson
- goredis
- collection
- sjson
- redigo
- aliyunoss
- 密碼
- 對稱加密
- 非對稱加密
- 單向散列函數
- 消息認證
- 數字簽名
- mysql優化
- 常見錯誤
- go run的錯誤
- 新手常見錯誤
- 中級錯誤
- 高級錯誤
- 常用工具
- 協程-泄露
- go env
- gometalinter代碼檢查
- go build
- go clean
- go test
- 包管理器
- go mod
- gopm
- go fmt
- pprof
- 提高編譯
- go get
- 代理
- 其他的知識
- go內存對齊
- 細節總結
- nginx路由匹配
- 一些博客
- redis為什么快
- cpu高速緩存
- 常用命令
- Go 永久阻塞的方法
- 常用技巧
- 密碼加密解密
- for 循環迭代變量
- 備注
- 垃圾回收
- 協程和纖程
- tar-gz
- 紅包算法
- 解決golang.org/x 下載失敗
- 逃逸分析
- docker
- 鏡像
- 容器
- 數據卷
- 網絡管理
- 網絡模式
- dockerfile
- docker-composer
- 微服務
- protoBuf
- GRPC
- tls
- consul
- micro
- crontab
- shell調用
- gorhill/cronexpr
- raft
- go操作etcd
- mongodb