The road of go learning (2) - the gorm/gin project realizes the creation of account and user information tables, uses bcrypt password encryption, and jwt realizes the login status verification function (1)

This section is inherited from the previous one. This article will create an account user information table, use bcrypt to implement password encryption, and jwt to implement the login status verification function.

Detailed reference address of this project code https://github.com/jiangbo66666/gin-vue-microBlog

1. Create account information, user information table and use the user_id in the account information table to associate two tables

In order to realize our login function, we need to store user information. Considering the problem that users contain too much information, I decided to use two tables, one account information table and one user information table. 表结构如下.

//性别类型
type Gender string

const (
	Male   Gender = "男"
	Female Gender = "女"
	other  Gender = "女"
)

// 用户信息表
type UserInfo struct {
    
    
	ID          uint      `gorm:"primaryKey;autoIncrement;comment:'用户id';uniqueIndex"` //id主键,自增
	Name        string    `gorm:"comment:'用户姓名'"`
	Sex         Gender    `gorm:"type:enum('男', '女', '其他');comment:'用户性别'"` //创建枚举类型
	BirthDay    time.Time `gorm:"comment:'用户生日'"`
	PhoneNumber string    `gorm:"comment:'用户手机号码'"`
	Email       string    `gorm:"comment:'用户邮箱'"`
	Address     string    `gorm:"comment:'用户地址'"`
	CreateBy    int       `gorm:"comment:'用户由谁创建'"`
	CreateAt    time.Time `gorm:"default:(NOW());comment:'创建账号时间'"` //创建默认时间
	UpdateAt    time.Time `gorm:"comment:'更新时间'"`
	RecentLogin time.Time `gorm:"comment:'最近登陆时间'"`
	HeaderImage string    `gorm:"comment:'头像地址'"`
	Profile     string    `gorm:"comment:'个人简介'"`
}

type AccountInfo struct {
    
    
	ID          uint      `gorm:"primaryKey;autoIncrement;comment:'账号id'"` //id主键,自增
	UserId      uint      `gorm:"comment:'用户id'"`
	PhoneNumber string    `gorm:"comment:'用户手机号码';uniqueIndex;type:varchar(20)"` //账号密码表带入手机号码,方便登录流程,减少登录查询
	AccountName string    `gorm:"comment:'账号名';uniqueIndex;type:varchar(20)"`
	Password    string    `gorm:"comment:'账号密码'"`
	CreateAt    time.Time `gorm:"comment:'创建时间'"`
	UpdateAt    time.Time `gorm:"comment:更新时间"`
	RecentLogin time.Time `gorm:"comment:最近登录时间"`
	Status      int       `gorm:"default:0;comment:'用户账号状态'"`
	User        UserInfo  `gorm:"foreignKey:UserId"` //外键关联userInfo表
}

For detailed function realization, see https://github.com/jiangbo66666/gin-vue-microBlog

And use gorm DB.AutoMigrate(AccountInfo{})to perform operations on the above account information table, use the AccountInfo useridfield as a foreign key to associate the user information table, and create two empty tables
Note:
First of all, I consider that the account name and mobile phone number may be used for query operations when logging in, so a unique index is added to the account name and mobile phone number to optimize the query speed.

	至此我们的表结构就建立完毕。

2. Use bcrypt to encrypt the account password

In the account information table, we need to store the user's account password. However, the password cannot be stored in plain text, and there is a risk of being stolen. So consider using bcrypta random salt value to encrypt the password of the account and store it.

First, we execute the following method on the terminal to load the bcrypt package

go get -u golang.org/x/crypto/bcrypt 

And introduce it in the project, and create the following two methods: realize the encryption of plaintext passwords, and verify

import "golang.org/x/crypto/bcrypt"

// 密码加密
func PasswordHash(pwd string) (string, error) {
    
    
	// GenerateFromPassword 方法对密码进行加密操作
	bytes, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
	if err != nil {
    
    
		return "", err
	}

	return string(bytes), err
}

// 密码验证
func PasswordVerify(pwd, hash string) bool {
    
    
	// CompareHashAndPassword 方法将加密的数据与原始数据进行对比
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(pwd))

	return err == nil
}

For detailed function realization, see https://github.com/jiangbo66666/gin-vue-microBlog

Note:
The above PasswordHashmethod accepts a []byte type of plaintext password and returns an encrypted password of string type.
PasswordVerifyThe method accepts a plaintext password string and an encrypted password string. The function will compare the two password strings and return a Boolean verification result

至此我们的账号密码的加密解密功能就此实现。

3. Use jwt to verify the login status

The full name of jwt is json web token, which is an identity token, which itself consists of three parts
header :Token header, which records the type and signature algorithm of the entire token
payloadThe token payload records the saved subject information, and account information can be saved in this project.
signatureToken signature, the entire token is signed according to the signature algorithm of the header, and the signature can ensure that the token is not forged and tampered

How to use it in code?
First we need to download the jwt package,This package is the latest package, usage may be different from the past, this project code refers to the official document

go get -u github.com/golang-jwt/jwt/v5

Code

type MyClaims struct {
    
    
	AccountName string `json:"accountName"`
	jwt.RegisteredClaims
}

// 自定义秘钥
var mySecret = []byte("jiangbo")

// 生成token
func GenerateToken(Name string) (string, error) {
    
    
	claims := MyClaims{
    
    
		Name,
		jwt.RegisteredClaims{
    
    
			// 过期时间
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)),
			//签名
			Issuer: "microBlog",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 自定义秘钥
	tokenStr, err := token.SignedString(mySecret)
	if err == nil {
    
    
		return tokenStr, err
	}
	return "", errors.New("出错了")
}

func VarifyToken(tokenStr string) (string, error) {
    
    
	token, err := jwt.ParseWithClaims(tokenStr, &MyClaims{
    
    }, func(token *jwt.Token) (interface{
    
    }, error) {
    
    
		// 校验秘钥
		return mySecret, nil
	})
	// 校验失败的时候即返回失败
	if err != nil {
    
    
		return "", err
	}
	// 解析账号名称
	claims, ok := token.Claims.(*MyClaims)
	if ok {
    
    
		return claims.AccountName, nil
	} else {
    
    
		return "", errors.New("解析账号名称失败")
	}
}

For detailed function realization, see https://github.com/jiangbo66666/gin-vue-microBlog

GenerateTokenReceive a name in the form of a string, that is, the account password as a custom payload, which is used to compare account information when executing a query
VarifyTokenReceive a token in the form of a string, and return an account name in the form of a string and an err, which is used to verify whether the verification failed or not

In the business logic, we return the token of the current account after the login request, the code is as follows

//router包
r.POST("/login", api.LoginByName)

//api user包
// 账号名登录路由函数
func LoginByName(c *gin.Context) {
    
    
	var loginInfo account_service.LoginInfo
	bindJson(c, &loginInfo)
	//账号名登录校验,并且返回一个token
	token, err := loginInfo.LoginByNameAndToken()
	if err == nil {
    
    
		c.JSON(200, gin.H{
    
    
			"code": 200,
			"data": gin.H{
    
    "token": token},
			"msg":  "登陆成功",
		})
	} else {
    
    
		c.JSON(200, gin.H{
    
    
			"code": 500,
			"msg":  "登陆失败",
		})
	}
}

For detailed function realization, see https://github.com/jiangbo66666/gin-vue-microBlog

We can perform token verification in routing middleware for scenarios where token verification is required, and resolve account names for information query in this scenario

The routing processing code is as follows

	// 路由分组,读取userdetail handler
	user := r.Group("/api/user")
	{
    
    
		user.Use(func(ctx *gin.Context) {
    
    
			token := ctx.GetHeader("Token")
			//校验账号信息
			AccountName, err := util.VarifyToken(token)
			if err != nil {
    
    
				ctx.JSON(200, gin.H{
    
    
					"code": 501,
					"msg":  "登录失效",
				})

				ctx.Abort()
				return
			} else {
    
    
				// token校验通过,将token中的账号信息存储起来
				ctx.Set("AccountName", AccountName)
				ctx.Next()
			}
		})
		user.POST("/info", api.UserDetail)
	}

For detailed function realization, see https://github.com/jiangbo66666/gin-vue-microBlog

Guess you like

Origin blog.csdn.net/weixin_42425391/article/details/129583500