Upon receiving the username and password at the login endpoint, it compares them with the information in the database. If correct, it generates and returns a Token
, otherwise, it prompts "Username or password error."
Similarly, it follows the development principle of Three Boards: writing Api
to generate Controller
, writing core logic Logic
, and having the Controller
call Logic
.
Add Api
api/users/v1/users.go
...
type LoginReq struct {
g.Meta `path:"users/login" method:"post" sm:"Login" tags:"User"`
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:"Add Authorization: token in the header for authentication-required interfaces"`
}
Don't forget to execute
gf gen ctrl
! You need to execute it every time you change theApi
, not repeated in the text below.
Write Logic
The difficulty of the login logic lies in generating the Token
. Prepare a random string JwtKey
as the signature, and define it in the utility
directory.
utility/jwt.go
package utility
var JwtKey = []byte("db03d23b03ec405793b38f10592a2f34")
Write the core logic. First, perform a Where
query based on the username. After data retrieval, encrypt the password again. If it matches the encrypted text in the database, it's a legitimate user and a Token
is generated and returned.
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 (u *Users) 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("Username or password error")
}
if user.Id == 0 {
return "", errors.New("User does not exist")
}
// Encrypt the password and compare it with the password in the database
if user.Password != encryptPassword(password) {
return "", errors.New("Username or password error")
}
// Generate token
uc := &userClaims{
Id: user.Id,
Username: user.Username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(6 * time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc)
return token.SignedString(utility.JwtKey)
}
As seen in the above code, we need to declare a struct UserClaims
to store the signature information. It saves the Id
and Username
and sets the token validity to 6 hours. The final declared object needs to call the SignedString
method and pass in JwtKey
to generate a signature.
Controller calls 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 := c.users.Login(ctx, req.Username, req.Password)
if err != nil {
return
}
return &v1.LoginRes{Token: token}, nil
}
Interface Testing
$ 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"
}
}