AuthController.java
9.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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;
}
}