鉴权
This commit is contained in:
67
src/main/java/cn/van/business/controller/BaseController.java
Normal file
67
src/main/java/cn/van/business/controller/BaseController.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package cn.van.business.controller;
|
||||
|
||||
import cn.van.business.model.user.UserInfo;
|
||||
import cn.van.business.util.JwtUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BaseController extends AbstractController {
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
// 获取当前用户信息
|
||||
protected UserInfo getCurrentUser(HttpServletRequest request) {
|
||||
String token = extractToken(request);
|
||||
if (token != null && jwtUtils.validateToken(token)) {
|
||||
String username = jwtUtils.extractUsername(token);
|
||||
// 从数据库获取用户信息
|
||||
return loadUserInfo(username);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查用户是否已登录
|
||||
protected boolean isLogin(HttpServletRequest request) {
|
||||
return getCurrentUser(request) != null;
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
protected String getCurrentUserId(HttpServletRequest request) {
|
||||
UserInfo user = getCurrentUser(request);
|
||||
return user != null ? user.getId() : null;
|
||||
}
|
||||
|
||||
// 获取当前用户角色
|
||||
protected List<String> getCurrentUserRoles(HttpServletRequest request) {
|
||||
UserInfo user = getCurrentUser(request);
|
||||
return user != null ? user.getRoles() : Collections.emptyList();
|
||||
}
|
||||
|
||||
// 提取token
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 加载用户信息
|
||||
private UserInfo loadUserInfo(String username) {
|
||||
// 实现用户信息加载
|
||||
return new UserInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.van.business.controller.jd;
|
||||
|
||||
import cn.van.business.filter.RequireAuth;
|
||||
import cn.van.business.model.ApiResponse;
|
||||
import cn.van.business.model.jd.ProductOrder;
|
||||
import cn.van.business.mq.MessageProducerService;
|
||||
@@ -21,35 +22,18 @@ import java.util.List;
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/recordOrder")
|
||||
@RequireAuth
|
||||
public class OrderController {
|
||||
|
||||
public static String TOKEN = "dd7f298cc465e7a9791796ae1303a1f6c031a18d4894ecd9e75d44fdb0ef32d80a49e4c5811b98d8ac65d73d22a54083630d5329c7c9de2276317669ecb2e544";
|
||||
@Autowired
|
||||
private JDUtil jdUtils;
|
||||
@Autowired
|
||||
private MessageProducerService messageProducerService;
|
||||
@Autowired
|
||||
private ProductOrderRepository productOrderRepository;
|
||||
|
||||
public boolean checkToken(String token) {
|
||||
return TOKEN.equals(token);
|
||||
}
|
||||
|
||||
@RequestMapping("/testToken")
|
||||
@ResponseBody
|
||||
public ApiResponse testToken(String token) {
|
||||
if (!checkToken(token)){
|
||||
return ApiResponse.unauthorized();
|
||||
}
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@RequestMapping("/list")
|
||||
@ResponseBody
|
||||
public ApiResponse list(String token) throws Exception {
|
||||
if (!checkToken(token)){
|
||||
return ApiResponse.unauthorized();
|
||||
}
|
||||
public ApiResponse list() throws Exception {
|
||||
try {
|
||||
|
||||
List<ProductOrder> all = productOrderRepository.findAll();
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package cn.van.business.controller.login;
|
||||
|
||||
import cn.van.business.model.user.LoginRequest;
|
||||
import cn.van.business.model.user.LoginResponse;
|
||||
import cn.van.business.service.CaptchaService;
|
||||
import cn.van.business.util.JwtUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final CaptchaService captchaService;
|
||||
private final JwtUtils jwtUtils;
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
public AuthController(CaptchaService captchaService, JwtUtils jwtUtils, AuthenticationManager authenticationManager, StringRedisTemplate redisTemplate) {
|
||||
this.captchaService = captchaService;
|
||||
this.jwtUtils = jwtUtils;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
private static final String USER_TOKEN_PREFIX = "user:token:";
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
|
||||
// 1. 基础校验
|
||||
if (StringUtils.isBlank(request.getUsername()) || StringUtils.isBlank(request.getPassword())) {
|
||||
throw new RuntimeException("用户名或密码不能为空");
|
||||
}
|
||||
|
||||
// 2. 验证码校验
|
||||
if (!captchaService.validateCaptcha(request.getCaptcha(), request.getGeneratedCaptcha())) {
|
||||
throw new RuntimeException("验证码错误");
|
||||
}
|
||||
// 在验证码校验后、认证前添加防爆破逻辑
|
||||
String loginFailKey = "login:fail:" + request.getUsername();
|
||||
Long failCount = redisTemplate.opsForValue().increment(loginFailKey);
|
||||
if (failCount != null && failCount > 5) {
|
||||
throw new RuntimeException("尝试次数过多,请稍后再试");
|
||||
}
|
||||
|
||||
// 3. 用户认证(Spring Security 标准流程)
|
||||
Authentication authentication = authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
|
||||
|
||||
// 4. 生成 JWT Token
|
||||
String token = jwtUtils.generateToken(request.getUsername());
|
||||
String refreshToken = generateRefreshToken(request.getUsername());
|
||||
|
||||
// 5. 存入 Redis
|
||||
saveUserToken(request.getUsername(), token, jwtUtils.getExpiration(token));
|
||||
|
||||
// 6. 构造响应
|
||||
LoginResponse response = new LoginResponse();
|
||||
response.setToken(token);
|
||||
response.setRefreshToken(refreshToken);
|
||||
response.setUsername(request.getUsername());
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<Void> logout(@RequestHeader("Authorization") String token) {
|
||||
String actualToken = token.startsWith("Bearer ") ? token.substring(7) : token;
|
||||
String username = jwtUtils.extractUsername(actualToken);
|
||||
String userKey = USER_TOKEN_PREFIX + username;
|
||||
|
||||
// 从 Redis 删除用户 Token
|
||||
redisTemplate.delete(userKey);
|
||||
|
||||
// 添加 Token 到黑名单
|
||||
addToBlacklist(actualToken, jwtUtils.getRemainingExpirationTime(actualToken));
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/captcha")
|
||||
public ResponseEntity<String> getCaptcha() throws Exception {
|
||||
String captchaImage = captchaService.generateCaptchaImage();
|
||||
return ResponseEntity.ok(captchaImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将用户 Token 存入 Redis,并设置相同有效期
|
||||
*/
|
||||
private void saveUserToken(String username, String token, long expiration) {
|
||||
String key = USER_TOKEN_PREFIX + username;
|
||||
redisTemplate.opsForValue().set(key, token, expiration, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Token 加入黑名单
|
||||
*/
|
||||
private void addToBlacklist(String token, long remainingTime) {
|
||||
String blacklistKey = "blacklist:token:" + token;
|
||||
redisTemplate.opsForValue().set(blacklistKey, "invalid", remainingTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成刷新 Token(Refresh Token)
|
||||
*/
|
||||
private String generateRefreshToken(String username) {
|
||||
return jwtUtils.generateToken(username);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.van.business.controller.login;
|
||||
|
||||
import cn.van.business.service.CaptchaService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/captcha")
|
||||
public class CaptchaController {
|
||||
|
||||
private final CaptchaService captchaService;
|
||||
|
||||
public CaptchaController(CaptchaService captchaService) {
|
||||
this.captchaService = captchaService;
|
||||
}
|
||||
|
||||
@GetMapping("/generate")
|
||||
public ResponseEntity<String> generateCaptcha() throws Exception {
|
||||
String base64Image = captchaService.generateCaptchaImage();
|
||||
return ResponseEntity.ok(base64Image);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user