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 userid
campo 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 bcrypt
um 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 PasswordHash
método acima aceita um tipo []byte de senha de texto simples e retorna uma senha criptografada do tipo string.
PasswordVerify
O 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
payload
:A carga útil do token registra as informações do assunto salvas e as informações da conta podem ser salvas neste projeto.
signature
:Assinatura 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
GenerateToken
Receba 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
VarifyToken
Receba 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