Golang加密字元串并存儲到SQLite資料庫
這是一段 Golang 代碼,它實作了以下功能:
- 從标準輸入讀取使用者名和密碼;
- 将密碼進行 bcrypt 加密,并将使用者名和加密後的密碼存儲到 SQLite 資料庫中;
- 查詢資料庫,擷取指定使用者名的記錄,并将其輸出到日志中。
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/crypto/bcrypt"
)
//定義了一個名為 `Users` 的結構體類型,包含使用者名和密碼兩個字段。
type Users struct {
Username string
Password []byte
}
//定義了一個名為 `encrypt_str` 的函數,接受使用者名和密碼兩個字元串作為輸入參數,将密碼使用 bcrypt 加密,并将使用者名和加密後的密碼封裝到一個 `Users` 結構體中傳回。
func encrypt_str(username, password string) (*Users, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
user := &Users{
Username: username,
Password: hashedPassword,
}
return user, nil
}
//定義了一個名為 `checkPassword` 的函數,接受一個 `Users` 結構體和一個密碼字元串作為輸入參數,使用 bcrypt 比較輸入的密碼和結構體中儲存的密碼是否比對,并傳回比較結果。
func checkPassword(user *Users, password string) bool {
err := bcrypt.CompareHashAndPassword(user.Password, []byte(password))
return err == nil
}
//定義了一個名為 `readInput` 的函數,從标準輸入中讀取使用者名和密碼,并傳回兩個字元串。
func readInput() (name string, p string) {
// 讀取标準輸入
var username, pwd string
fmt.Printf("請輸入使用者名: ")
// fmt.Println("請輸入一行文字: ")
fmt.Scanln(&username)
fmt.Printf("請輸入密碼: ")
fmt.Scanln(&pwd)
return username, pwd
}
//定義了一個名為 `QueryUser` 的函數,接受一個 `sql.DB` 類型的資料庫連接配接對象和一個字元串類型的使用者名作為輸入參數,查詢資料庫中是否存在指定的使用者名,并傳回查詢結果的bool值。
func QueryUser(db *sql.DB, username string) bool {
s, err3 := db.Prepare("select * from users where username = ?")
if err3 != nil {
log.Fatal(err3)
}
var id int
var n string
var pwd string
defer s.Close()
err := s.QueryRow(username).Scan(&id, &n, &pwd)
if err == sql.ErrNoRows {
return true
}
return false
}
func main() {
// 打開了一個名為users.db的SQLite資料庫,并建立了一個名為users的資料表
db, err := sql.Open("sqlite3", "./users.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 建立users資料表
_, err = db.Exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT unique, password TEXT)")
if err != nil {
log.Fatal(err)
}
//調用readInput函數讀取使用者輸入,并使用encrypt_str函數加密密碼
name, p := readInput()
// fmt.Scanln(&input)
// fmt.Printf("username: %s\t password: %s\n", username, pwd)
u, err2 := encrypt_str(name, p)
if err2 != nil {
log.Fatal(err2)
}
// fmt.Printf("u: %v\n", u)
// 調用QueryUser函數查詢資料庫中是否已經存在該使用者,如果不存在,将使用者名和加密後的密碼插入到資料庫中
b := QueryUser(db, u.Username)
if b {
// 将資料存儲到資料庫中
stmt, err := db.Prepare("INSERT INTO users(username, password) VALUES(?, ?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec(u.Username, u.Password)
if err != nil {
log.Fatal(err)
}
fmt.Println("資料已成功存儲到資料庫中")
}
// 查詢資料表
rows, err := db.Query("SELECT * FROM users where username = ?", u.Username)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var (
id int64
username string
password string
)
if err := rows.Scan(&id, &username, &password); err != nil {
log.Fatal(err)
}
log.Printf("id: %d \tname: %s \tpassword: %s\n", id, username, password)
}
// fmt.Printf("%v", rows)
}
以下是對該代碼的詳細說明:
這段代碼主要做的是使用者輸入使用者名和密碼,程式會對密碼進行加密,然後将使用者名和加密後的密碼存儲到 SQLite 資料庫中。如果輸入的使用者名已經存在于資料庫中,則不會再次存儲到資料庫中。程式還支援查詢資料庫中已有的使用者資訊。
以下是對代碼的詳細分析:
- 導入需要的包
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/crypto/bcrypt"
)
程式需要使用 database/sql 包來操作 SQLite 資料庫,還需要使用 github.com/mattn/go-sqlite3 包來連接配接 SQLite 資料庫。同時,還需要使用 golang.org/x/crypto/bcrypt 包來對使用者密碼進行加密。
- 定義 Users 結構體
type Users struct {
Username string
Password []byte
}
定義了一個名為 Users 的結構體,用來存儲使用者的使用者名和密碼。密碼是一個位元組數組,因為加密後的密碼是無法直接顯示的字元串。
- 實作密碼加密和密碼驗證的函數
func encrypt_str(username, password string) (*Users, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
user := &Users{
Username: username,
Password: hashedPassword,
}
return user, nil
}
func checkPassword(user *Users, password string) bool {
err := bcrypt.CompareHashAndPassword(user.Password, []byte(password))
return err == nil
}
encrypt_str 函數用來将使用者密碼加密,函數接收使用者名和明文密碼作為參數,傳回一個指向 Users 結構體的指針和一個可能發生的錯誤。加密的過程中,使用 bcrypt.GenerateFromPassword 函數生成一個哈希值,并将使用者名和哈希值存儲到 Users 結構體中。
checkPassword 函數用來驗證使用者密碼是否正确。函數接收一個 Users 結構體指針和明文密碼作為參數,如果明文密碼和 Users 結構體中的哈希值比對,則傳回 true;否則傳回 false。
- 定義讀取使用者輸入的函數
func readInput() (name string, p string) {
var username, pwd string
fmt.Printf("請輸入使用者名: ")
fmt.Scanln(&username)
fmt.Printf("請輸入密碼: ")
fmt.Scanln(&pwd)
return username, pwd
}
readInput 函數用來讀取使用者輸入的使用者名和密碼,并傳回這兩個值。
- 定義查詢使用者的函數
func QueryUser(db *sql.DB, username string) bool {
s, err3 := db.Prepare("select * from users where username = ?")
if err3 != nil {
log.Fatal(err3)
}
var id int
var n string
var pwd string
defer s.Close()
err := s.QueryRow(username).Scan(&id, &n, &pwd)
if err == sql.ErrNoRows {
return true
}
return false
}
在main函數中,首先建立了一個sqlite3資料庫,如果資料庫建立失敗,會直接調用log.Fatal函數将錯誤列印并退出程式。然後,程式調用了readInput函數,擷取使用者輸入的使用者名和密碼,并調用encrypt_str函數對密碼進行加密。接下來,程式調用了QueryUser函數,查詢資料庫中是否已經存在該使用者。如果存在,則不需要将該使用者資料再次存儲到資料庫中;如果不存在,則程式将該使用者資料存儲到資料庫中。
最後,查詢資料表,将使用者的資料列印出來。
在encrypt_str函數中,程式使用bcrypt對密碼進行加密,生成的哈希值被存儲在一個Users類型的結構體中。在checkPassword函數中,程式使用bcrypt.CompareHashAndPassword函數來檢查密碼是否正确。在readInput函數中,程式使用fmt.Scanln函數讀取标準輸入。
在QueryUser函數中,程式通過查詢username字段來判斷資料庫中是否存在該使用者,如果不存在,則傳回true;否則傳回false。在main函數中,程式首先調用QueryUser函數查詢資料庫中是否存在該使用者,然後根據查詢結果決定是否将該使用者資料存儲到資料庫中。最後,程式使用db.Query函數查詢資料表,并将查詢結果列印出來。