258 lines
10 KiB
Java
258 lines
10 KiB
Java
|
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("无效的刷新令牌");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|