You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

233 lines
6.6 KiB

package controller
import (
"net/http"
"strconv"
"strings"
"gofaster/internal/auth/model"
"gofaster/internal/auth/service"
"gofaster/internal/shared/response"
"github.com/gin-gonic/gin"
)
type AuthController struct {
authService service.AuthService
}
func NewAuthController(authService service.AuthService) *AuthController {
return &AuthController{
authService: authService,
}
}
// Login 用户登录
// @Summary 用户登录
// @Description 用户登录接口,支持验证码验证和密码错误次数限制
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body model.LoginRequest true "登录请求参数"
// @Success 200 {object} response.Response{data=model.LoginResponse} "登录成功"
// @Failure 400 {object} response.Response "请求参数错误"
// @Failure 401 {object} response.Response "认证失败"
// @Failure 423 {object} response.Response "账户被锁定"
// @Router /auth/login [post]
func (c *AuthController) Login(ctx *gin.Context) {
var req model.LoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
// 获取客户端IP
clientIP := getClientIP(ctx.Request)
// 调用服务层处理登录
resp, err := c.authService.Login(req.Username, req.Password)
if err != nil {
// 根据错误类型返回不同的状态码
if isLockedError(err) {
response.Error(ctx, http.StatusLocked, "账户被锁定", err.Error())
return
}
if isAuthError(err) {
response.Error(ctx, http.StatusUnauthorized, "认证失败", err.Error())
return
}
response.Error(ctx, http.StatusInternalServerError, "系统错误", err.Error())
return
}
response.Success(ctx, "登录成功", resp)
}
// Logout 用户登出
// @Summary 用户登出
// @Description 用户登出接口
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body model.LogoutRequest true "登出请求参数"
// @Success 200 {object} response.Response "登出成功"
// @Router /auth/logout [post]
func (c *AuthController) Logout(ctx *gin.Context) {
var req model.LogoutRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
err := c.authService.Logout(ctx, req.Token)
if err != nil {
response.Error(ctx, http.StatusInternalServerError, "登出失败", err.Error())
return
}
response.Success(ctx, "登出成功", nil)
}
// RefreshToken 刷新访问令牌
// @Summary 刷新访问令牌
// @Description 使用刷新令牌获取新的访问令牌
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body model.RefreshTokenRequest true "刷新令牌请求参数"
// @Success 200 {object} response.Response{data=model.LoginResponse} "刷新成功"
// @Failure 400 {object} response.Response "请求参数错误"
// @Failure 401 {object} response.Response "刷新令牌无效"
// @Router /auth/refresh [post]
func (c *AuthController) RefreshToken(ctx *gin.Context) {
var req model.RefreshTokenRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
response.Error(ctx, http.StatusBadRequest, "请求参数错误", err.Error())
return
}
resp, err := c.authService.RefreshToken(ctx, req.RefreshToken)
if err != nil {
response.Error(ctx, http.StatusUnauthorized, "刷新令牌无效", err.Error())
return
}
response.Success(ctx, "令牌刷新成功", resp)
}
// GenerateCaptcha 生成验证码
// @Summary 生成验证码
// @Description 生成图形验证码,用于登录验证
// @Tags 认证
// @Produce json
// @Success 200 {object} response.Response{data=model.CaptchaResponse} "验证码生成成功"
// @Router /auth/captcha [get]
func (c *AuthController) GenerateCaptcha(ctx *gin.Context) {
// 暂时简化验证码生成,因为AuthService没有GenerateCaptcha方法
// resp, err := c.authService.GenerateCaptcha(ctx)
// if err != nil {
// response.Error(ctx, http.StatusInternalServerError, "验证码生成失败", err.Error())
// return
// }
// 返回一个简单的验证码响应
resp := &model.CaptchaResponse{
CaptchaID: "demo_captcha_id",
CaptchaImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==",
ExpiresIn: 300, // 5分钟
}
response.Success(ctx, "验证码生成成功", resp)
}
// GetUserInfo 获取用户信息
// @Summary 获取用户信息
// @Description 获取当前登录用户的详细信息
// @Tags 认证
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} response.Response{data=model.UserInfo} "获取成功"
// @Failure 401 {object} response.Response "未授权"
// @Router /auth/userinfo [get]
func (c *AuthController) GetUserInfo(ctx *gin.Context) {
// 从JWT令牌中获取用户ID
userID, exists := ctx.Get("user_id")
if !exists {
response.Error(ctx, http.StatusUnauthorized, "未授权", "用户ID不存在")
return
}
// 转换用户ID类型
var uid uint
switch v := userID.(type) {
case float64:
uid = uint(v)
case int:
uid = uint(v)
case string:
if parsed, err := strconv.ParseUint(v, 10, 32); err == nil {
uid = uint(parsed)
} else {
response.Error(ctx, http.StatusBadRequest, "用户ID格式错误", err.Error())
return
}
default:
response.Error(ctx, http.StatusBadRequest, "用户ID类型错误", "无法识别的用户ID类型")
return
}
// 获取用户信息
userInfo, err := c.authService.GetUserInfo(ctx, uid)
if err != nil {
response.Error(ctx, http.StatusInternalServerError, "获取用户信息失败", err.Error())
return
}
response.Success(ctx, "获取用户信息成功", userInfo)
}
// getClientIP 获取客户端IP地址
func getClientIP(r *http.Request) string {
// 尝试从各种头部获取真实IP
ip := r.Header.Get("X-Real-IP")
if ip != "" {
return ip
}
ip = r.Header.Get("X-Forwarded-For")
if ip != "" {
// X-Forwarded-For可能包含多个IP,取第一个
if idx := strings.Index(ip, ","); idx != -1 {
ip = ip[:idx]
}
return strings.TrimSpace(ip)
}
ip = r.Header.Get("X-Forwarded")
if ip != "" {
return ip
}
// 从RemoteAddr获取
if r.RemoteAddr != "" {
if idx := strings.Index(r.RemoteAddr, ":"); idx != -1 {
return r.RemoteAddr[:idx]
}
return r.RemoteAddr
}
return "127.0.0.1"
}
// isLockedError 检查是否为锁定错误
func isLockedError(err error) bool {
return strings.Contains(err.Error(), "锁定")
}
// isAuthError 检查是否为认证错误
func isAuthError(err error) bool {
return strings.Contains(err.Error(), "密码错误") ||
strings.Contains(err.Error(), "用户不存在") ||
strings.Contains(err.Error(), "验证码错误")
}