AuthController.java 9.7 KB
package com.aigeo.auth.controller;

import com.aigeo.auth.dto.LoginRequest;
import com.aigeo.auth.dto.LoginResponse;
import com.aigeo.auth.dto.RegisterRequest;
import com.aigeo.auth.dto.RefreshTokenRequest;
import com.aigeo.auth.service.AuthService;
import com.aigeo.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;

/**
 * 认证控制器
 * 
 * 提供用户认证相关的API接口,包括:
 * - 用户登录认证
 * - 用户注册
 * - Token刷新
 * - 用户登出
 * - 密码重置等功能
 *
 * @author AIGEO Team
 * @since 1.0.0
 */
@Tag(name = "用户认证", description = "用户认证和授权管理接口")
@Slf4j
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    @Operation(summary = "用户登录", description = "使用用户名/邮箱和密码进行登录认证")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "登录成功"),
        @ApiResponse(responseCode = "400", description = "参数错误"),
        @ApiResponse(responseCode = "401", description = "用户名或密码错误"),
        @ApiResponse(responseCode = "423", description = "账户已被锁定")
    })
    @PostMapping("/login")
    public Result<LoginResponse> login(
            @Parameter(description = "登录请求参数", required = true)
            @Valid @RequestBody LoginRequest request,
            HttpServletRequest httpRequest) {
        
        log.info("用户登录尝试: {}", request.getUsername());
        
        // 获取客户端信息
        String ipAddress = getClientIpAddress(httpRequest);
        String userAgent = httpRequest.getHeader("User-Agent");
        
        LoginResponse response = authService.login(request, ipAddress, userAgent);
        
        log.info("用户登录成功: {}, IP: {}", request.getUsername(), ipAddress);
        return Result.success(response);
    }

    @Operation(summary = "用户注册", description = "注册新用户账户")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "注册成功"),
        @ApiResponse(responseCode = "400", description = "参数错误或用户已存在"),
        @ApiResponse(responseCode = "409", description = "用户名或邮箱已被使用")
    })
    @PostMapping("/register")
    public Result<LoginResponse> register(
            @Parameter(description = "注册请求参数", required = true)
            @Valid @RequestBody RegisterRequest request,
            HttpServletRequest httpRequest) {
        
        log.info("用户注册尝试: {}, 邮箱: {}", request.getUsername(), request.getEmail());
        
        String ipAddress = getClientIpAddress(httpRequest);
        String userAgent = httpRequest.getHeader("User-Agent");
        
        LoginResponse response = authService.register(request, ipAddress, userAgent);
        
        log.info("用户注册成功: {}", request.getUsername());
        return Result.success(response);
    }

    @Operation(summary = "刷新访问令牌", description = "使用刷新令牌获取新的访问令牌")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "令牌刷新成功"),
        @ApiResponse(responseCode = "400", description = "参数错误"),
        @ApiResponse(responseCode = "401", description = "刷新令牌无效或已过期")
    })
    @PostMapping("/refresh")
    public Result<LoginResponse> refreshToken(
            @Parameter(description = "刷新令牌请求参数", required = true)
            @Valid @RequestBody RefreshTokenRequest request) {
        
        log.debug("令牌刷新请求");
        
        LoginResponse response = authService.refreshToken(request.getRefreshToken());
        
        log.debug("令牌刷新成功");
        return Result.success(response);
    }

    @Operation(summary = "用户登出", description = "用户主动登出,使令牌失效")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "登出成功"),
        @ApiResponse(responseCode = "401", description = "未授权")
    })
    @PostMapping("/logout")
    public Result<Void> logout(
            @Parameter(description = "Authorization头中的Bearer token", required = true)
            @RequestHeader("Authorization") String authHeader,
            HttpServletRequest httpRequest) {
        
        String token = extractTokenFromHeader(authHeader);
        if (token == null) {
            return Result.error("无效的Authorization头格式");
        }
        
        String ipAddress = getClientIpAddress(httpRequest);
        authService.logout(token, ipAddress);
        
        log.info("用户登出成功, IP: {}", ipAddress);
        return Result.success();
    }

    @Operation(summary = "获取当前用户信息", description = "根据token获取当前登录用户的基本信息")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "获取成功"),
        @ApiResponse(responseCode = "401", description = "未授权或token无效")
    })
    @GetMapping("/me")
    public Result<Object> getCurrentUser(
            @Parameter(description = "Authorization头中的Bearer token", required = true)
            @RequestHeader("Authorization") String authHeader) {
        
        String token = extractTokenFromHeader(authHeader);
        if (token == null) {
            return Result.error("无效的Authorization头格式");
        }
        
        Object userInfo = authService.getCurrentUserInfo(token);
        
        return Result.success(userInfo);
    }

    @Operation(summary = "验证令牌有效性", description = "验证访问令牌是否有效且未过期")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "令牌有效"),
        @ApiResponse(responseCode = "401", description = "令牌无效或已过期")
    })
    @PostMapping("/validate")
    public Result<Boolean> validateToken(
            @Parameter(description = "Authorization头中的Bearer token", required = true)
            @RequestHeader("Authorization") String authHeader) {
        
        String token = extractTokenFromHeader(authHeader);
        if (token == null) {
            return Result.error("无效的Authorization头格式");
        }
        
        boolean isValid = authService.validateToken(token);
        
        return Result.success(isValid);
    }

    @Operation(summary = "发送密码重置邮件", description = "向用户邮箱发送密码重置链接")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "邮件发送成功"),
        @ApiResponse(responseCode = "404", description = "用户不存在"),
        @ApiResponse(responseCode = "429", description = "请求过于频繁")
    })
    @PostMapping("/forgot-password")
    public Result<Void> forgotPassword(
            @Parameter(description = "用户邮箱地址", required = true, example = "user@example.com")
            @RequestParam String email,
            HttpServletRequest httpRequest) {
        
        log.info("密码重置请求: {}", email);
        
        String ipAddress = getClientIpAddress(httpRequest);
        authService.sendPasswordResetEmail(email, ipAddress);
        
        // 为了安全考虑,无论用户是否存在都返回成功
        return Result.success();
    }

    @Operation(summary = "重置密码", description = "使用重置令牌设置新密码")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "密码重置成功"),
        @ApiResponse(responseCode = "400", description = "参数错误或令牌无效"),
        @ApiResponse(responseCode = "410", description = "重置令牌已过期")
    })
    @PostMapping("/reset-password")
    public Result<Void> resetPassword(
            @Parameter(description = "重置令牌", required = true)
            @RequestParam String token,
            @Parameter(description = "新密码", required = true)
            @RequestParam String newPassword,
            HttpServletRequest httpRequest) {
        
        log.info("密码重置执行, token: {}", token.substring(0, Math.min(token.length(), 10)) + "...");
        
        String ipAddress = getClientIpAddress(httpRequest);
        authService.resetPassword(token, newPassword, ipAddress);
        
        log.info("密码重置成功, IP: {}", ipAddress);
        return Result.success();
    }

    /**
     * 从HTTP请求中获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIP = request.getHeader("X-Real-IP");
        if (xRealIP != null && !xRealIP.isEmpty() && !"unknown".equalsIgnoreCase(xRealIP)) {
            return xRealIP;
        }
        
        return request.getRemoteAddr();
    }

    /**
     * 从Authorization头中提取token
     */
    private String extractTokenFromHeader(String authHeader) {
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        return null;
    }
}