feat: 初始化 kenaito-notebook 桌面记事本项目
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidCredentials = errors.New("邮箱或密码错误")
|
||||
ErrUserExists = errors.New("邮箱已被注册")
|
||||
ErrInvalidToken = errors.New("无效的 token")
|
||||
ErrTokenExpired = errors.New("token 已过期")
|
||||
)
|
||||
|
||||
// JWT 密钥,生产环境应从环境变量或配置文件读取
|
||||
var jwtSecret = []byte("kenaito-notebook-jwt-secret-2026")
|
||||
|
||||
type jwtHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Typ string `json:"typ"`
|
||||
}
|
||||
|
||||
type jwtPayload struct {
|
||||
UserID int64 `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
Exp int64 `json:"exp"`
|
||||
Iat int64 `json:"iat"`
|
||||
}
|
||||
|
||||
type AuthService struct{}
|
||||
|
||||
func NewAuthService() *AuthService {
|
||||
return &AuthService{}
|
||||
}
|
||||
|
||||
// HashPassword 使用 bcrypt 哈希密码
|
||||
func (s *AuthService) HashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
return string(bytes), err
|
||||
}
|
||||
|
||||
// CheckPassword 验证密码
|
||||
func (s *AuthService) CheckPassword(password, hash string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GenerateToken 生成 JWT token
|
||||
func (s *AuthService) GenerateToken(userID int64, email string) (string, error) {
|
||||
now := time.Now()
|
||||
payload := jwtPayload{
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
Exp: now.Add(7 * 24 * time.Hour).Unix(), // 7 天过期
|
||||
Iat: now.Unix(),
|
||||
}
|
||||
|
||||
// Header
|
||||
header := jwtHeader{Alg: "HS256", Typ: "JWT"}
|
||||
headerJSON, err := json.Marshal(header)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
headerB64 := base64.RawURLEncoding.EncodeToString(headerJSON)
|
||||
|
||||
// Payload
|
||||
payloadJSON, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payloadB64 := base64.RawURLEncoding.EncodeToString(payloadJSON)
|
||||
|
||||
// 签名
|
||||
signingInput := headerB64 + "." + payloadB64
|
||||
mac := hmac.New(sha256.New, jwtSecret)
|
||||
mac.Write([]byte(signingInput))
|
||||
signature := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
|
||||
return signingInput + "." + signature, nil
|
||||
}
|
||||
|
||||
// ValidateToken 验证 JWT token,返回 payload
|
||||
func (s *AuthService) ValidateToken(tokenStr string) (*jwtPayload, error) {
|
||||
parts := strings.Split(tokenStr, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
signingInput := parts[0] + "." + parts[1]
|
||||
|
||||
// 验证签名
|
||||
mac := hmac.New(sha256.New, jwtSecret)
|
||||
mac.Write([]byte(signingInput))
|
||||
expectedSig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
|
||||
if !hmac.Equal([]byte(parts[2]), []byte(expectedSig)) {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
// 解析 payload
|
||||
payloadJSON, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
var payload jwtPayload
|
||||
if err := json.Unmarshal(payloadJSON, &payload); err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
// 检查过期
|
||||
if time.Now().Unix() > payload.Exp {
|
||||
return nil, ErrTokenExpired
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
// ExtractTokenID 从 token 字符串中提取用户 ID(不验证签名,仅用于快速检查)
|
||||
func ExtractTokenID(token string) (int64, error) {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return 0, fmt.Errorf("invalid token format")
|
||||
}
|
||||
payloadJSON, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var payload jwtPayload
|
||||
if err := json.Unmarshal(payloadJSON, &payload); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return payload.UserID, nil
|
||||
}
|
||||
Reference in New Issue
Block a user