登录接口收到用户名和密码后,与数据库中的信息做比对,如果正确则生成Token
返回,否则提示”用户名或密码错误“。
同样遵循三板斧开发原则:编写Api
生成Controller
,编写核心逻辑Logic
,Controller
调起Logic
。
添加Api
api/users/v1/users.go
...
type LoginReq struct {
g.Meta `path:"users/login" method:"post" sm:"登录" tags:"用户"`
Username string `json:"username" v:"required|length:3,12"`
Password string `json:"password" v:"required|length:6,16"`
}
type LoginRes struct {
Token string `json:"token" dc:"在需要鉴权的接口中header加入Authorization: token"`
}
别忘记执行
gf gen ctrl
哦!每次变更Api
都需要执行它,后文不再重复提示。
编写Logic
登录逻辑的的难点在于生成Token
。准备好一个随机字符串JwtKey
用作签名,我们将其定义在utility
目录下。
utility/jwt.go
package utility
var JwtKey = []byte("db03d23b03ec405793b38f10592a2f34")
编写核心逻辑,先根据用户名进行Where
查询,获取到数据后,将密码再次加密,如果和数据库中的密文一致则说明是合法用户,生成Token
返回。
internal/logic/users/account.go
package users
import (
"context"
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
"star/internal/dao"
"star/internal/model/entity"
"star/utility"
)
type UserClaims struct {
Id uint
Username string
jwt.RegisteredClaims
}
func Login(ctx context.Context, username, password string) (tokenString string, err error) {
var user entity.Users
err = dao.Users.Ctx(ctx).Where("username", username).Scan(&user)
if err != nil {
return "", errors.New("用户名或密码错误")
}
if user.Id == 0 {
return "", errors.New("用户不存在")
}
// 将密码加密后与数据库中的密码进行比对
if user.Password != encryptPassword(password) {
return "", errors.New("用户名或密码错误")
}
// 生成token
userClaims := &UserClaims{
Id: user.Id,
Username: user.Username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(6 * time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, userClaims)
return token.SignedString(utility.JwtKey)
}
从上面的代码可以看到,我们需要声明一个结构体UserClaims
保存签名信息,这里保存了Id
和Username
并设置了Token
有效期为 6 个小时。最后的声明对象还需要调用SignedString
方法传入JwtKey
生成签名。
Controller调用Logic
internal/controller/users/users_v1_login.go
package users
import (
"context"
"star/internal/logic/users"
"star/api/users/v1"
)
func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) {
token, err := users.Login(ctx, req.Username, req.Password)
if err != nil {
return
}
return &v1.LoginRes{Token: token}, nil
}
接口测试
$ curl -X POST http://127.0.0.1:8000/v1/users/login -H "Content-Type: application/json" -d "{\"username\":\"oldme\", \"password\":\"123456\"}"
{
"code":0,
"message":"",
"data":{
"token":"eyJhbGciOi...ZY_ATzOU"
}
}