stdproject/backend/src/main/java/com/stdproject/controller/AuthController.java
2025-05-30 09:58:52 +08:00

258 lines
10 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.stdproject.controller;
import com.stdproject.common.Constants;
import com.stdproject.common.OperationLog;
import com.stdproject.common.Result;
import com.stdproject.entity.AppUser;
import com.stdproject.service.IAppUserService;
import com.stdproject.utils.CaptchaUtils;
import com.stdproject.utils.JwtUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 认证控制器
*
* @author StdProject
*/
@Tag(name = "认证管理", description = "用户登录、登出、验证码等认证相关接口")
@RestController
@RequestMapping("/auth")
@Slf4j
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private CaptchaUtils captchaUtils;
@Autowired
private IAppUserService appUserService;
/**
* 登录请求类
*/
public static class LoginRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
@NotBlank(message = "验证码不能为空")
private String captcha;
@NotBlank(message = "验证码Key不能为空")
private String captchaKey;
// Getters and Setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getCaptcha() { return captcha; }
public void setCaptcha(String captcha) { this.captcha = captcha; }
public String getCaptchaKey() { return captchaKey; }
public void setCaptchaKey(String captchaKey) { this.captchaKey = captchaKey; }
}
/**
* 修改密码请求类
*/
public static class ChangePasswordRequest {
@NotBlank(message = "原密码不能为空")
private String oldPassword;
@NotBlank(message = "新密码不能为空")
private String newPassword;
// Getters and Setters
public String getOldPassword() { return oldPassword; }
public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
public String getNewPassword() { return newPassword; }
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
}
/**
* 生成验证码
*/
@Operation(summary = "生成验证码", description = "生成图形验证码")
@GetMapping("/captcha")
public Result<Map<String, String>> generateCaptcha() {
try {
// 生成验证码
CaptchaUtils.CaptchaResult captchaResult = captchaUtils.generateCaptcha();
// 生成验证码Key
String captchaKey = UUID.randomUUID().toString();
// 注意:验证码应当存储在会话中或其他存储中,此处省略存储步骤
// 在实际应用中可以使用Session或其他方式存储验证码
Map<String, String> result = new HashMap<>();
result.put("captchaKey", captchaKey);
result.put("captchaImage", captchaResult.getImageBase64());
result.put("code", captchaResult.getCode()); // 临时方案:直接返回验证码(生产环境不建议)
return Result.success(result);
} catch (Exception e) {
log.error("生成验证码失败: {}", e.getMessage(), e);
return Result.error("生成验证码失败");
}
}
/**
* 用户登录
*/
@Operation(summary = "用户登录", description = "用户登录认证")
@PostMapping("/login")
@OperationLog(type = Constants.OPT_TYPE_LOGIN, module = "认证管理", description = "用户登录")
public Result<Map<String, Object>> login(@Valid @RequestBody LoginRequest loginRequest, HttpServletRequest request) {
try {
// 注意:在实际应用中,应该从会话或其他存储中获取验证码进行验证
// 此处简化处理,假设验证码已通过(实际应用中需要实现验证逻辑)
// 如果使用了临时方案,可以从前端传回验证码进行比对
// 进行身份认证
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
AppUser user=appUserService.findByUsername(loginRequest.getUsername());
// 设置认证信息到安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成JWT Token
String token = jwtUtils.generateToken(user.getUsername(),user.getId());
Map<String, Object> result = new HashMap<>();
result.put("token", token);
result.put("userInfo", user);
return Result.success(result);
} catch (Exception e) {
log.error("用户登录失败: {}", e.getMessage(), e);
return Result.error("登录失败: " + e.getMessage());
}
}
/**
* 用户登出
*/
@Operation(summary = "用户登出", description = "用户登出")
@PostMapping("/logout")
@OperationLog(type = Constants.OPT_TYPE_OTHER, module = "认证管理", description = "用户登出")
public Result<Void> logout(HttpServletRequest request) {
try {
// 获取Token
String token = request.getHeader(Constants.JWT_HEADER);
if (token != null && token.startsWith(Constants.JWT_PREFIX)) {
token = token.substring(Constants.JWT_PREFIX.length());
// 注意在实际应用中应该实现Token失效机制
// 可以考虑使用短期Token或其他方式实现Token失效
// 此处省略Token黑名单实现
}
// 清除安全上下文
SecurityContextHolder.clearContext();
return Result.success();
} catch (Exception e) {
log.error("用户登出失败: {}", e.getMessage(), e);
return Result.error("登出失败");
}
}
/**
* 获取当前用户信息
*/
@Operation(summary = "获取当前用户信息", description = "获取当前登录用户的详细信息")
@GetMapping("/userinfo")
public Result<AppUser> getCurrentUserInfo(HttpServletRequest request) {
try {
String token = request.getHeader(Constants.JWT_HEADER);
if (token != null && token.startsWith(Constants.JWT_PREFIX)) {
token = token.substring(Constants.JWT_PREFIX.length());
String username = jwtUtils.getUsernameFromToken(token);
AppUser userInfo = appUserService.findByUsername(username);
return Result.success(userInfo);
}
return Result.unauthorized();
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage(), e);
return Result.error("获取用户信息失败");
}
}
/**
* 修改密码
*/
@Operation(summary = "修改密码", description = "修改当前用户密码")
@PostMapping("/changePassword")
@OperationLog(type = Constants.OPT_TYPE_UPDATE, module = "认证管理", description = "修改密码")
public Result<Void> changePassword(@Valid @RequestBody ChangePasswordRequest request, HttpServletRequest httpRequest) {
try {
String token = httpRequest.getHeader(Constants.JWT_HEADER);
if (token != null && token.startsWith(Constants.JWT_PREFIX)) {
token = token.substring(Constants.JWT_PREFIX.length());
String username = jwtUtils.getUsernameFromToken(token);
boolean success = appUserService.changePassword(username, request.getOldPassword(), request.getNewPassword());
if (success) {
return Result.success();
} else {
return Result.error("原密码错误");
}
}
return Result.unauthorized();
} catch (Exception e) {
log.error("修改密码失败: {}", e.getMessage(), e);
return Result.error("修改密码失败");
}
}
@PostMapping("/refreshToken")
public Result<String> refreshToken(@RequestBody Map<String, String> params) {
String refreshToken = params.get("refreshToken");
if (refreshToken == null || refreshToken.trim().isEmpty()) {
return Result.error("刷新令牌不能为空");
}
// 验证refreshToken的有效性并获取用户信息
String username = jwtUtils.getUsernameFromToken(refreshToken);
if (username != null) {
AppUser user = appUserService.findByUsername(username);
// 生成新的token
return Result.success(jwtUtils.generateToken(user.getUsername(), user.getId()));
}
return Result.error("无效的刷新令牌");
}
}