Gin 框架之用户密码加密
一、引入
Gin是一个用Go语言编写的Web框架,而用户密码的加密通常是在应用程序中处理用户身份验证时的一个重要问题。
通常敏感信息你要防两类人:
- 研发人员:包括你自己和你的同事。作为研发人员,你可能会接触到公司的敏感信息,如用户数据、商业机密等。
- 攻击者:指那些有意获取或破坏敏感信息的人。他们可能是黑客、竞争对手、内部威胁等。
所以为了增加安全性,密码通常不应以明文形式存储在数据库中,而是应该经过适当的加密处理。
二、密码加密位置
实际上,你选择 service
、repository
、dao
,包括 domain
都可以:
service
加密:加密是一个业务概念,不是一个存储概念。repository
加密:加密是一个存储概念,毕竟我们说的是“加密存储”。dao
加密:加密是一个数据库概念,因为我完全可以选择利用数据库本身的加密功能来实现。domain
加密:加密是一个业务概念,但是应该是“用户(User)”自己才知道怎么加密。
三、如何加密
加密算法的选择会直接影响你整个系统的安全性,因为攻击者一旦拿到了密码,差不多就可以为所欲为了。
选择加密算法的标准就一个,难破解。你要考虑以下问题:
- 相同的密码,加密后的结果应该不同。你可以预期,很多用户习惯用
123456
这种密码,但是我们希望数据库存储的值还是不一样。 - 难以通过碰撞、彩虹表来破解。
常见的加密算法无非就是下面这些,安全性逐步提高:
-
md5
之类的哈希算法。 -
在 1 的基础上,引入了盐值(
salt
),或者进行多次哈希等。 -
PBKDF2
、BCrypt
这一类随机盐值的加密算法,同样的文本加密后的结果都不同。
四、bcrypt 库加密
4.1 介绍
在Go语言中,可以使用bcrypt库来对密码进行安全加密,号称最安全的加密算法。
4.2 优点:
-
不需要你自己去生成盐值。
-
不需要额外存储盐值。
-
可以通过控制
cost
来控制加密性能。 -
同样的文本,加密后的结果不同。
4.3 使用
首先,你需要在Go中安装bcrypt库:
go get golang.org/x/crypto/bcrypt
下面是一个使用bcrypt库在对用户密码进行加密的示例:
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
// 用户注册时使用的密码
password := "user_password"
// 使用bcrypt库对密码进行哈希处理
hashedPassword, err := hashPassword(password)
if err != nil {
fmt.Println("Error hashing password:", err)
return
}
fmt.Println("Original Password:", password)
fmt.Println("Hashed Password:", hashedPassword)
// 模拟用户登录时的密码验证
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库的GenerateFromPassword函数进行哈希处理
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}
func comparePasswords(hashedPassword, inputPassword string) error {
// 使用bcrypt库的CompareHashAndPassword函数比较密码
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(inputPassword))
return err
}
加密后的结果如下:
五、小黄书密码加密实践
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
}
分享是一种快乐,开心是一种态度!