Files
platform/web/handlers/login.go

146 lines
3.0 KiB
Go

package handlers
import (
"errors"
"platform/web/auth"
m "platform/web/models"
q "platform/web/queries"
s "platform/web/services"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
type LoginReq struct {
Username string `json:"username"`
Password string `json:"password"`
Remember bool `json:"remember"`
}
type LoginResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Expires int64 `json:"expires"`
Auth s.AuthContext `json:"auth"`
Profile *m.User `json:"profile"`
}
func Login(c *fiber.Ctx) error {
// 验证请求参数
req := new(LoginReq)
if err := c.BodyParser(req); err != nil {
return err
}
if req.Username == "" {
return fiber.NewError(fiber.StatusBadRequest, "手机号不能为空")
}
if req.Password == "" {
return fiber.NewError(fiber.StatusBadRequest, "验证码不能为空")
}
return loginByPhone(c, req)
}
func loginByPhone(c *fiber.Ctx, req *LoginReq) error {
// 验证验证码
err := s.Verifier.VerifySms(c.Context(), req.Username, req.Password)
if err != nil {
if errors.Is(err, s.ErrVerifierServiceInvalid) {
return fiber.NewError(fiber.StatusBadRequest, "验证码错误")
}
return err
}
// 查找用户 todo 获取权限信息
var user *m.User
err = q.Q.Transaction(func(tx *q.Query) error {
user, err = tx.User.
Where(tx.User.Phone.Eq(req.Username)).
Take()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
// 如果用户不存在,初始化用户 todo 保存默认权限信息
if user == nil {
user = &m.User{
Phone: req.Username,
Username: req.Username,
}
}
// 更新用户的登录时间
user.LastLogin = time.Now()
user.LastLoginHost = c.IP()
user.LastLoginAgent = c.Get("User-Agent")
if err := tx.User.Omit(q.User.AdminID).Save(user); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// 保存到会话
auth := s.AuthContext{
Permissions: map[string]struct{}{
"user": {},
},
Payload: s.Payload{
Id: user.ID,
Type: s.PayloadUser,
Name: user.Name,
Avatar: user.Avatar,
},
}
duration := time.Hour * 24
if req.Remember {
duration *= 7
}
token, err := s.Session.Create(c.Context(), auth)
if err != nil {
return err
}
user.Password = ""
return c.JSON(LoginResp{
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Expires: token.AccessTokenExpires.Unix(),
Auth: auth,
Profile: user,
})
}
type LogoutReq struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
func Logout(c *fiber.Ctx) error {
_, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
if err != nil {
// 用户未登录
return nil
}
// 解析请求参数
req := new(LogoutReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 删除会话
err = s.Session.Remove(c.Context(), req.AccessToken, req.RefreshToken)
if err != nil {
return err
}
return nil
}