参考资料:https://astaxie.gitbooks.io/build-web-application-with-golang/content/zh/09.5.html
- 方案1:bcrypt
- 方案2:scrypt(参考资料中的专家方案)
本文采用方案1进行明文密码的加密操作,Bcrypt是单向Hash加密算法,此算法对于同一个明文密码,每次生成的hash不一样,每次加密,都会采用不同的盐值来进行加密,最后返回的 hash 值包含盐值等信息的密文。
其中:$是分割符,无意义;2a是bcrypt加密版本号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。
- 安装 crypto/bcrypt
go get -u golang.org/x/crypto/bcrypt
- 生成hash值
bcrypt.GenerateFromPassword([]byte(password), 10) 复制代码
- 10 为 cost 默认值,加密级别系数,越大越安全但性能开下也随之增大
- 密码验证
bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 复制代码
- 参数1 为保存在数据库中的密码hash(之前加密后的密码)
- 参数2 为前端传过来要验证的密码
- 返回值 返回true说明密码验证通过
- 完整示例
package main import ( "fmt" "golang.org/x/crypto/bcrypt" ) func HashPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10) return string(bytes), err } func CheckPasswordHash(hash, password string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil } func main() { password := "123456" hash, _ := HashPassword(password) fmt.Println("Password:", password) fmt.Println("Hash: ", hash) match := CheckPasswordHash(hash, password) fmt.Println("Match: ", match) } 复制代码
运行结果:
Password: 123456 Hash: $2a$10$O.do8guW0m5PAphJuuMH7eVSTRuZAtVNsrLLORH6pMzFWdqCmc2Wu Match: true 复制代码
gorm 钩子介绍使用
参考资料 gorm.io/zh_CN/docs/hooks.html
使用钩子来实现密码加密后存库,我们之前使用的方式 data.Password = BcryptPW(data.Password)
,替换成钩子后,框架会在写入库前,自动调用钩子函数,来将密码进行加密处理:
加密及钩子在项目中的使用
model/User.go
package model import ( "ginVue3blog/utils/errmsg" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" "log" ) type User struct { gorm.Model Username string `gorm:"type:varchar(20);not null " json:"username" validate:"required,min=4,max=12" label:"用户名"` Password string `gorm:"type:varchar(500);not null" json:"password" validate:"required,min=6,max=120" label:"密码"` Role int `gorm:"type:int;DEFAULT:2" json:"role" validate:"required,gte=2" label:"角色码"` } //...... // CreateUser 新增用户 func CreateUser(data *User) int { //data.Password = BcryptPW(data.Password) err := db.Create(&data).Error if err != nil { return errmsg.ERROR //500 } return errmsg.SUCCSE } // ChangePassword 修改密码 func ChangePassword(id int, data *User) int { //var user User //var maps = make(map[string]interface{}) //maps["password"] = data.Password err = db.Select("password").Where("id=?", id).Updates(&data).Error if err != nil { return errmsg.ERROR } return errmsg.SUCCSE } //使用钩子 BeforeCreate 密码加密&权限控制 func (u *User) BeforeCreate(_ *gorm.DB) (err error) { u.Password = BcryptPW(u.Password) u.Role = 2 return nil } func (u *User) BeforeUpdate(_ *gorm.DB) (err error) { u.Password = BcryptPW(u.Password) return nil } // BcryptPW 生成密码 func BcryptPW(password string) string { const cost = 10 //加密级别系数,越大越安全但性能开下也随之增大 HashPw, err := bcrypt.GenerateFromPassword([]byte(password), cost) if err != nil { log.Fatal(err) } return string(HashPw) } // CheckLoginFront 前台登录 func CheckLoginFront(username, password string) (User, int) { var user User var PasswordErr error db.Where("username=?", username).First(&user) PasswordErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) if user.ID == 0 { return user, errmsg.ERROR_USER_NOT_EXIST } if PasswordErr != nil { return user, errmsg.ERROR_PASSWORD_WRONG } return user, errmsg.SUCCSE } //......