Gin 框架之用戶密碼加密

一、引入

Gin是一個(gè)用Go語(yǔ)言編寫的Web框架,而用戶密碼的加密通常是在應(yīng)用程序中處理用戶身份驗(yàn)證時(shí)的一個(gè)重要問題。

通常敏感信息你要防兩類人:

  • 研發(fā)人員:包括你自己和你的同事。作為研發(fā)人員,你可能會(huì)接觸到公司的敏感信息,如用戶數(shù)據(jù)、商業(yè)機(jī)密等。
  • 攻擊者:指那些有意獲取或破壞敏感信息的人。他們可能是黑客、競(jìng)爭(zhēng)對(duì)手、內(nèi)部威脅等。

所以為了增加安全性,密碼通常不應(yīng)以明文形式存儲(chǔ)在數(shù)據(jù)庫(kù)中,而是應(yīng)該經(jīng)過適當(dāng)?shù)募用芴幚怼?/p>

二、密碼加密位置

實(shí)際上,你選擇 service、repositorydao,包括 domain 都可以:

  • service 加密:加密是一個(gè)業(yè)務(wù)概念,不是一個(gè)存儲(chǔ)概念。
  • repository 加密:加密是一個(gè)存儲(chǔ)概念,畢竟我們說的是“加密存儲(chǔ)”。
  • dao 加密:加密是一個(gè)數(shù)據(jù)庫(kù)概念,因?yàn)槲彝耆梢赃x擇利用數(shù)據(jù)庫(kù)本身的加密功能來實(shí)現(xiàn)。
  • domain 加密:加密是一個(gè)業(yè)務(wù)概念,但是應(yīng)該是“用戶(User)”自己才知道怎么加密。

三、如何加密

加密算法的選擇會(huì)直接影響你整個(gè)系統(tǒng)的安全性,因?yàn)楣粽咭坏┠玫搅嗣艽a,差不多就可以為所欲為了。

選擇加密算法的標(biāo)準(zhǔn)就一個(gè),難破解。你要考慮以下問題:

  • 相同的密碼,加密后的結(jié)果應(yīng)該不同。你可以預(yù)期,很多用戶習(xí)慣用 123456 這種密碼,但是我們希望數(shù)據(jù)庫(kù)存儲(chǔ)的值還是不一樣。
  • 難以通過碰撞、彩虹表來破解。

常見的加密算法無非就是下面這些,安全性逐步提高:

  1. md5 之類的哈希算法。

  2. 在 1 的基礎(chǔ)上,引入了鹽值(salt),或者進(jìn)行多次哈希等。

  3. PBKDF2BCrypt 這一類隨機(jī)鹽值的加密算法,同樣的文本加密后的結(jié)果都不同。

四、bcrypt 庫(kù)加密

4.1 介紹

在Go語(yǔ)言中,可以使用bcrypt庫(kù)來對(duì)密碼進(jìn)行安全加密,號(hào)稱最安全的加密算法。

4.2 優(yōu)點(diǎn):

  • 不需要你自己去生成鹽值。

  • 不需要額外存儲(chǔ)鹽值。

  • 可以通過控制 cost 來控制加密性能。

  • 同樣的文本,加密后的結(jié)果不同。

4.3 使用

首先,你需要在Go中安裝bcrypt庫(kù):

go get golang.org/x/crypto/bcrypt

下面是一個(gè)使用bcrypt庫(kù)在對(duì)用戶密碼進(jìn)行加密的示例:

package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    // 用戶注冊(cè)時(shí)使用的密碼
    password := "user_password"

    // 使用bcrypt庫(kù)對(duì)密碼進(jìn)行哈希處理
    hashedPassword, err := hashPassword(password)
    if err != nil {
        fmt.Println("Error hashing password:", err)
        return
    }

    fmt.Println("Original Password:", password)
    fmt.Println("Hashed Password:", hashedPassword)

    // 模擬用戶登錄時(shí)的密碼驗(yàn)證
    err = comparePasswords(hashedPassword, "wrong_password")
    if err != nil {
        fmt.Println("Password does not match:", err)
    } else {
        fmt.Println("Password matches!")
    }

    err = comparePasswords(hashedPassword, "user_password")
    if err != nil {
        fmt.Println("Password does not match:", err)
    } else {
        fmt.Println("Password matches!")
    }
}

func hashPassword(password string) (string, error) {
    // 使用bcrypt庫(kù)的GenerateFromPassword函數(shù)進(jìn)行哈希處理
    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    if err != nil {
        return "", err
    }
    return string(hashedPassword), nil
}

func comparePasswords(hashedPassword, inputPassword string) error {
    // 使用bcrypt庫(kù)的CompareHashAndPassword函數(shù)比較密碼
    err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(inputPassword))
    return err
}

加密后的結(jié)果如下:

五、小黃書密碼加密實(shí)踐

webook/internal/service/user.go:

func (svc *UserService) SignUp(ctx context.Context, u domain.User) error {
    // 先加密密碼
    hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
    if err != nil {
        return err
    }
    u.Password = string(hash)
    // 然后存起來
    return svc.repo.Create(ctx, u)
}

func (svc *UserService) Login(ctx context.Context, email, password string) (domain.User, error) {
    // 先找用戶
    u, err := svc.repo.FindByEmail(ctx, email)
    if err == repository.ErrUserNotFound {
        return domain.User{}, ErrInvalidUserOrPassword
    }
    if err != nil {
        return domain.User{}, err
    }
    // 比較密碼了
    err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
    if err != nil {
        return domain.User{}, ErrInvalidUserOrPassword
    }
    return u, nil
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容