You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
5.8 KiB
Go
228 lines
5.8 KiB
Go
1 month ago
|
package auth
|
||
|
|
||
|
import (
|
||
|
"cls/internal/application/auth"
|
||
|
"cls/internal/interfaces"
|
||
|
"cls/pkg/jwtfx"
|
||
|
"cls/pkg/logger"
|
||
|
"fmt"
|
||
|
jwt "github.com/appleboy/gin-jwt/v2"
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"net/http"
|
||
|
|
||
|
auth_middleware "cls/internal/infrastructure/middleware/auth"
|
||
|
)
|
||
|
|
||
|
type AuthHandler struct {
|
||
|
captchaService *auth.CaptchaService
|
||
|
service *auth.AuthService
|
||
|
log logger.Logger
|
||
|
}
|
||
|
|
||
|
var _ interfaces.Handler = (*AuthHandler)(nil)
|
||
|
|
||
|
func NewAuthHandler(captcha *auth.CaptchaService, service *auth.AuthService, log logger.New) *AuthHandler {
|
||
|
return &AuthHandler{captcha, service, log("cls:interfaces:auth")}
|
||
|
}
|
||
|
|
||
|
// getUserIdentifier 从 JWT token 中获取用户标识
|
||
|
func (h *AuthHandler) getUserIdentifier(c *gin.Context) string {
|
||
|
claims := jwt.ExtractClaims(c)
|
||
|
if claims["phone"] != nil {
|
||
|
return claims["phone"].(string)
|
||
|
}
|
||
|
if claims["guest_id"] != nil {
|
||
|
return claims["guest_id"].(string)
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// isGuest 判断当前用户是否为游客
|
||
|
func (h *AuthHandler) isGuest(c *gin.Context) bool {
|
||
|
claims := jwt.ExtractClaims(c)
|
||
|
return claims["guest_id"] != nil
|
||
|
}
|
||
|
|
||
|
// GetSmsCaptcha 获取短信验证码
|
||
|
func (h *AuthHandler) GetSmsCaptcha(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Phone string `json:"phone" binding:"required"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数"})
|
||
|
return
|
||
|
}
|
||
|
// 从 JWT token 中获取用户标识
|
||
|
username := jwtfx.GetUserIdentifier(c)
|
||
|
if username == "" {
|
||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "未获取到用户标识"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 生成短信验证码
|
||
|
captcha, err := h.captchaService.GenerateSmsCaptcha(username, req.Phone)
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
c.JSON(http.StatusOK, gin.H{
|
||
|
"message": "验证码已发送",
|
||
|
"resetAfter": captcha.ResetAfter,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// GetImageCaptcha 获取图片验证码
|
||
|
func (h *AuthHandler) GetImageCaptcha(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Phone string `json:"phone"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindQuery(&req); err != nil {
|
||
|
fmt.Println(err.Error())
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fmt.Println("获取图片验证码")
|
||
|
|
||
|
// 生成验证码
|
||
|
captcha, err := h.captchaService.GenerateSlideVerify(req.Phone)
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
c.JSON(http.StatusOK, captcha)
|
||
|
}
|
||
|
|
||
|
// VerifySmsCaptcha 验证短信验证码
|
||
|
func (h *AuthHandler) VerifySmsCaptcha(c *gin.Context) {
|
||
|
c.JSON(http.StatusOK, gin.H{"message": "验证成功"})
|
||
|
}
|
||
|
|
||
|
func (h *AuthHandler) VerifyImageCaptcha(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Token string `json:"token" binding:"required"`
|
||
|
X int `json:"x" binding:"required"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 1. 先验证图片验证码
|
||
|
if b, err := h.captchaService.VerifySlide(req.Token, req.X); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "图片验证码错误"})
|
||
|
return
|
||
|
} else {
|
||
|
c.JSON(http.StatusOK, b)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func (h *AuthHandler) LoginCaptcha(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Phone string `json:"phone" binding:"required"`
|
||
|
SmsCode string `json:"smsCode" binding:"required"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 从 JWT token 中获取用户标识
|
||
|
username := jwtfx.GetUserIdentifier(c)
|
||
|
if username == "" {
|
||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "未获取到用户标识"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 验证验证码
|
||
|
err := h.captchaService.VerifySmsCaptcha(username, req.Phone, req.SmsCode)
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
}
|
||
|
token, err := h.service.LoginByCaptcha(req.Phone)
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
} else {
|
||
|
c.JSON(http.StatusOK, token)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func (h *AuthHandler) LoginPassword(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Phone string `json:"phone" binding:"required"`
|
||
|
CaptchaCode string `json:"captcha_code" binding:"required"`
|
||
|
Password string `json:"password" binding:"required"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 从 JWT token 中获取用户标识
|
||
|
username := jwtfx.GetUserIdentifier(c)
|
||
|
if username == "" {
|
||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "未获取到用户标识"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
token, err := h.service.LoginByPassword(req.Phone, req.Password)
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
|
||
|
} else {
|
||
|
c.JSON(http.StatusOK, token)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func (h *AuthHandler) RegisterRouters(app gin.IRouter) {
|
||
|
auth := app.Group("/auth")
|
||
|
{
|
||
|
auth.POST("/login-captcha", h.LoginCaptcha)
|
||
|
auth.POST("/login-password", h.LoginPassword)
|
||
|
auth.POST("/image-captcha", h.GetImageCaptcha)
|
||
|
auth.POST("/sms-captcha", h.GetSmsCaptcha)
|
||
|
auth.POST("/verify-image", h.VerifyImageCaptcha)
|
||
|
auth.POST("/verify-sms", h.VerifySmsCaptcha)
|
||
|
auth.POST("/decode-token", h.DecodeToken)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DecodeToken 解析 token 并返回原始的加密手机号
|
||
|
func (h *AuthHandler) DecodeToken(c *gin.Context) {
|
||
|
var req struct {
|
||
|
Token string `json:"token" binding:"required"`
|
||
|
}
|
||
|
|
||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 通过 auth 模块获取密钥
|
||
|
secretKey := h.service.GetJWTSecretKey()
|
||
|
|
||
|
// 调用 DecodeTokenStatic 方法解析 token
|
||
|
encryptedPhone, err := auth_middleware.DecodeTokenStatic(req.Token, []byte(secretKey))
|
||
|
if err != nil {
|
||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
c.JSON(http.StatusOK, gin.H{
|
||
|
"encryptedPhone": encryptedPhone,
|
||
|
})
|
||
|
}
|