gin博客项目复盘--04用户密码加密

285 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

参考资料:https://astaxie.gitbooks.io/build-web-application-with-golang/content/zh/09.5.html

本文采用方案1进行明文密码的加密操作,Bcrypt是单向Hash加密算法,此算法对于同一个明文密码,每次生成的hash不一样,每次加密,都会采用不同的盐值来进行加密,最后返回的 hash 值包含盐值等信息的密文。

其中:$是分割符,无意义;2a是bcrypt加密版本号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。

  • 安装 crypto/bcrypt
    • go get -u golang.org/x/crypto/bcrypt
  1. 生成hash值
bcrypt.GenerateFromPassword([]byte(password), 10)
  • 10 为 cost 默认值,加密级别系数,越大越安全但性能开下也随之增大
  1. 密码验证
bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 
  • 参数1 为保存在数据库中的密码hash(之前加密后的密码)
  • 参数2 为前端传过来要验证的密码
  • 返回值 返回true说明密码验证通过
  1. 完整示例
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
}
//......