Files
memora/memora-api/internal/service/auth.go
wsy182 613ce02e9a feat(layout): 更新应用布局和UI组件样式
- 重构App.vue中的侧边栏布局,更新Logo设计为带有标识和副标题的新样式
- 调整顶部导航栏,增加标题区域显示当前路由标题和日期
- 修改菜单项配置,更新导航标签为更直观的中文描述
- 在Home.vue中替换原有的仪表板为新的Hero卡片和项目进展展示
- 更新Memory.vue中的学习界面,添加学习计划设置和多阶段学习模式
- 集成新的API端点路径,将baseURL从/api调整为/api/v1
- 调整整体视觉风格,包括颜色主题、字体家族和响应式布局
- 更新数据库模型以支持词库功能,添加相关的数据迁移和种子数据
- 调整认证系统的用户ID类型从整型到字符串的变更
- 更改前端构建工具从npm到pnpm,并更新相应的Dockerfile配置
2026-02-27 16:16:57 +08:00

120 lines
2.7 KiB
Go

package service
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"
"memora-api/internal/model"
"memora-api/internal/request"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
type AuthService struct {
db *gorm.DB
}
func NewAuthService(db *gorm.DB) *AuthService {
return &AuthService{db: db}
}
func jwtSecret() []byte {
secret := os.Getenv("MEMORA_JWT_SECRET")
if secret == "" {
secret = "memora-dev-secret-change-me"
}
return []byte(secret)
}
func (s *AuthService) Register(req request.RegisterRequest) (*model.User, error) {
var exists model.User
if err := s.db.Where("email = ?", req.Email).First(&exists).Error; err == nil {
return nil, errors.New("邮箱已注册")
}
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
user := model.User{
Email: req.Email,
Name: req.Name,
PasswordHash: string(hash),
}
if err := s.db.Create(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func sign(data string) string {
mac := hmac.New(sha256.New, jwtSecret())
_, _ = mac.Write([]byte(data))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
func makeToken(uid string) string {
exp := time.Now().Add(7 * 24 * time.Hour).Unix()
payload := fmt.Sprintf("%s:%d", uid, exp)
encPayload := base64.RawURLEncoding.EncodeToString([]byte(payload))
sig := sign(encPayload)
return encPayload + "." + sig
}
func (s *AuthService) Login(req request.LoginRequest) (string, *model.User, error) {
var user model.User
if err := s.db.Where("email = ?", req.Email).First(&user).Error; err != nil {
return "", nil, errors.New("账号或密码错误")
}
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
return "", nil, errors.New("账号或密码错误")
}
return makeToken(user.ID), &user, nil
}
func ParseToken(tokenString string) (string, error) {
parts := strings.Split(tokenString, ".")
if len(parts) != 2 {
return "", errors.New("token无效")
}
encPayload, gotSig := parts[0], parts[1]
if sign(encPayload) != gotSig {
return "", errors.New("token无效")
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(encPayload)
if err != nil {
return "", errors.New("token无效")
}
payloadParts := strings.Split(string(payloadBytes), ":")
if len(payloadParts) != 2 {
return "", errors.New("token无效")
}
uid := payloadParts[0]
if uid == "" {
return "", errors.New("token无效")
}
exp, err := strconv.ParseInt(payloadParts[1], 10, 64)
if err != nil {
return "", errors.New("token无效")
}
if time.Now().Unix() > exp {
return "", errors.New("token已过期")
}
return uid, nil
}