The road of go learning (2) - o projeto gorm/gin realiza a criação da conta e da tabela de informações do usuário, usa criptografia de senha bcrypt e jwt realiza a função de verificação de status de login (1)

Esta seção é herdada da anterior.Este artigo criará uma tabela de informações do usuário da conta, usará bcrypt para implementar a criptografia de senha e jwt para implementar a função de verificação de status de login.

Endereço de referência detalhado deste código de projeto https://github.com/jiangbo66666/gin-vue-microBlog

1. Crie informações da conta, tabela de informações do usuário e use o user_id na tabela de informações da conta para associar duas tabelas

Para realizar nossa função de login, precisamos armazenar as informações do usuário. Considerando o problema de que os usuários contêm muitas informações, decidi usar duas tabelas, uma tabela de informações da conta e uma tabela de informações do usuário. 表结构如下.

//性别类型
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表
}

Para a realização detalhada da função, consulte https://github.com/jiangbo66666/gin-vue-microBlog

E use gorm DB.AutoMigrate(AccountInfo{})para executar operações na tabela de informações da conta acima, use o useridcampo AccountInfo como uma chave estrangeira para associar a tabela de informações do usuário e crie duas tabelas vazias
Nota:
Em primeiro lugar, considero que o nome da conta e o número de telemóvel podem ser utilizados para operações de consulta ao iniciar sessão, pelo que é adicionado um índice único ao nome da conta e ao número de telemóvel para otimizar a velocidade da consulta.

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

2. Use bcrypt para criptografar a senha da conta

Na tabela de informações da conta, precisamos armazenar a senha da conta do usuário. No entanto, a senha não pode ser armazenada em texto simples e existe o risco de ser roubada. Portanto, considere usar bcryptum valor salt aleatório para criptografar a senha da conta e armazená-la.

Primeiro, executamos o seguinte método no terminal para carregar o pacote bcrypt

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

E introduza-o no projeto e crie os dois métodos a seguir: realizar a criptografia de senhas de texto sem formatação e verificar

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
}

Para a realização detalhada da função, consulte https://github.com/jiangbo66666/gin-vue-microBlog

Observação:
O PasswordHashmétodo acima aceita um tipo []byte de senha de texto simples e retorna uma senha criptografada do tipo string.
PasswordVerifyO método aceita uma string de senha em texto simples e uma string de senha criptografada. A função irá comparar as duas strings de senha e retornar um resultado de verificação booleana

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

3. Use jwt para verificar o status de login

O nome completo de jwt é json web token, que é um token de identidade, que consiste em três partes
header :Cabeçalho do token, que registra o tipo e o algoritmo de assinatura de todo o token
payloadA carga útil do token registra as informações do assunto salvas e as informações da conta podem ser salvas neste projeto.
signatureAssinatura de token, todo o token é assinado de acordo com o algoritmo de assinatura do cabeçalho e a assinatura pode garantir que o token não seja forjado e adulterado

Como usá-lo no código?
Primeiro precisamos baixar o pacote jwt,Este pacote é o pacote mais recente, o uso pode ser diferente do passado, este código do projeto refere-se ao documento oficial

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

Código

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("解析账号名称失败")
	}
}

Para a realização detalhada da função, consulte https://github.com/jiangbo66666/gin-vue-microBlog

GenerateTokenReceba um nome na forma de uma string, ou seja, a senha da conta como uma carga útil personalizada, que é usada para comparar as informações da conta ao executar uma consulta
VarifyTokenReceba um token na forma de uma string e retorne um nome de conta na forma de uma string e um err, que é usado para verificar se a verificação falhou ou não

Na lógica de negócios, retornamos o token da conta atual após a solicitação de login, o código é o seguinte

//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":  "登陆失败",
		})
	}
}

Para a realização detalhada da função, consulte https://github.com/jiangbo66666/gin-vue-microBlog

Podemos executar a verificação de token no middleware de roteamento para cenários em que a verificação de token é necessária e resolver nomes de contas para consulta de informações neste cenário

O código de processamento de roteamento é o seguinte

	// 路由分组,读取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)
	}

Para a realização detalhada da função, consulte https://github.com/jiangbo66666/gin-vue-microBlog

Acho que você gosta

Origin blog.csdn.net/weixin_42425391/article/details/129583500
Recomendado
Clasificación