主备迁移只webflux

This commit is contained in:
2025-08-06 11:59:00 +08:00
parent 940f1d7bac
commit d2e479b9f8
7 changed files with 153 additions and 73 deletions

19
pom.xml
View File

@@ -35,16 +35,26 @@
</scm>
<properties>
<java.version>21</java.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-gateway</artifactId>-->
<!-- <version>4.3.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-webflux</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
@@ -148,7 +158,6 @@
<version>3.1.499</version> <!-- 推荐稳定版本 -->
</dependency>
</dependencies>
<build>

View File

@@ -27,6 +27,7 @@ public class SecurityConfig {
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);
return http.build();

View File

@@ -5,7 +5,6 @@ import com.onekeycall.videotablet.entity.User;
import com.onekeycall.videotablet.result.Result;
import com.onekeycall.videotablet.service.UserService;
import com.onekeycall.videotablet.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@@ -15,14 +14,12 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@RequestMapping("/public")
public class LoginController {
private final UserService userService;
@@ -39,7 +36,7 @@ public class LoginController {
this.authenticationManager = authenticationManager;
}
@PostMapping("/public/register")
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody RegisterRequest registerRequest) {
try {
userService.registerUser(registerRequest.getUsername(), registerRequest.getPassword());
@@ -49,7 +46,7 @@ public class LoginController {
}
}
@PostMapping("/public/login")
@PostMapping("/login")
public ResponseEntity<?> login(
@RequestParam(value = "user_id") String userId, @RequestParam String password,
@RequestParam(value = "device_id", required = false) String deviceId) {
@@ -91,7 +88,7 @@ public class LoginController {
}
@PostMapping("/public/registerByPhone")
@PostMapping("/phone_register")
public Result registerByPhone(
@RequestParam String phone, @RequestParam String code,
@RequestParam(value = "verify_key") String verifyKey, @RequestParam(value = "device_id") String deviceId) {
@@ -129,7 +126,7 @@ public class LoginController {
}
}
@PostMapping("/public/loginByPhone")
@PostMapping("/phone_login")
public Result loginByPhone(
@RequestParam String phone, @RequestParam String code,
@RequestParam(value = "verify_key") String verifyKey, @RequestParam(value = "device_id") String deviceId) {
@@ -161,54 +158,5 @@ public class LoginController {
}
}
@PostMapping("/public/setPasswordByPhone")
public Result setPasswordByPhone(
HttpServletRequest request, @RequestParam(value = "user_id") String userId,
@RequestParam String password, @RequestParam(value = "verify_password") String verifyPassword,
@RequestParam(value = "device_id") String deviceId) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7); // 提取真正的Token
if (StringUtils.equals(password, verifyPassword)) {
if (jwtUtil.validateAccessToken(userId, token, deviceId)) {
userService.setPasswordByUserId(userId, password);
return Result.ok().message("set password success");
} else {
return Result.error().message("token is not same");
}
} else {
return Result.error().message("password is not same");
}
} else {
return Result.error().message("Authorization header is incorrect");
}
}
@PostMapping("/public/changePassword")
public Result changePassword(
HttpServletRequest request,
@RequestParam(value = "user_id") String userId,
@RequestParam(value = "old_password") String oldPassword,
@RequestParam String password, @RequestParam(value = "verify_password") String verifyPassword,
@RequestParam(value = "device_id") String deviceId) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
if (StringUtils.equals(password, verifyPassword)) {
if (!oldPassword.equals(password)) {
if (jwtUtil.validateAccessToken(userId, token, deviceId)) {
return userService.changePassword(userId, oldPassword, password);
} else {
return Result.error().message("token is not same");
}
} else {
return Result.error().message("The old password and the new password are the same");
}
} else {
return Result.error().message("password is not same");
}
} else {
return Result.error().message("Authorization header is incorrect");
}
}
}

View File

@@ -0,0 +1,83 @@
package com.onekeycall.videotablet.controller;
import com.onekeycall.videotablet.result.Result;
import com.onekeycall.videotablet.service.UserService;
import com.onekeycall.videotablet.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserPasswordController {
private final UserService userService;
private final AuthenticationManager authenticationManager;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private JwtUtil jwtUtil;
@Autowired
public UserPasswordController(UserService userService, AuthenticationManager authenticationManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
}
@PostMapping("/phone_set_password")
public Result setPasswordByPhone(
HttpServletRequest request, @RequestParam(value = "user_id") String userId,
@RequestParam String password, @RequestParam(value = "verify_password") String verifyPassword,
@RequestParam(value = "device_id") String deviceId) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7); // 提取真正的Token
if (StringUtils.equals(password, verifyPassword)) {
if (jwtUtil.validateAccessToken(userId, token, deviceId)) {
userService.setPasswordByUserId(userId, password);
return Result.ok().message("set password success");
} else {
return Result.error().message("token is not same");
}
} else {
return Result.error().message("password is not same");
}
} else {
return Result.error().message("Authorization header is incorrect");
}
}
@PostMapping("/change_password")
public Result changePassword(
HttpServletRequest request,
@RequestParam(value = "user_id") String userId,
@RequestParam(value = "old_password") String oldPassword,
@RequestParam String password, @RequestParam(value = "verify_password") String verifyPassword,
@RequestParam(value = "device_id") String deviceId) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
if (StringUtils.equals(password, verifyPassword)) {
if (!oldPassword.equals(password)) {
if (jwtUtil.validateAccessToken(userId, token, deviceId)) {
return userService.changePassword(userId, oldPassword, password);
} else {
return Result.error().message("token is not same");
}
} else {
return Result.error().message("The old password and the new password are the same");
}
} else {
return Result.error().message("password is not same");
}
} else {
return Result.error().message("Authorization header is incorrect");
}
}
}

View File

@@ -9,12 +9,12 @@ import java.util.Collections;
import java.util.Date;
@Entity
@Table(name = "users")
@Table(name = "ordinary_users")
public class User implements UserDetails {
@Transient // 关键注解:声明此字段不映射到数据库
private boolean newUser;
@Transient // 关键注解:声明此字段不映射到数据库
@Transient
private boolean hasPassword;
@Id

View File

@@ -2,6 +2,7 @@ package com.onekeycall.videotablet.handler;
import com.onekeycall.videotablet.result.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
@@ -9,7 +10,9 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@@ -23,6 +26,14 @@ import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
// 替换为 WebFlux 的异常处理
// @ExceptionHandler(ResponseStatusException.class)
// public Mono<ResponseEntity<String>> handleResponseStatusException(ResponseStatusException ex) {
// return Mono.just(ResponseEntity
// .status(ex.getStatusCode())
// .body(ex.getReason()));
// }
/**
* 使用ExceptionHandler注解声明处理Exception异常
*
@@ -51,12 +62,12 @@ public class GlobalExceptionHandler {
@ResponseBody
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoResourceFoundException.class)
public Result handleNoResourceFoundException(NoResourceFoundException ex) {
public ResponseEntity<?> handleNoResourceFoundException(NoResourceFoundException ex) {
ex.printStackTrace();
// 新版本 Spring推荐我没有这个方法
// return ResponseEntity.notFound().body(error);
// 旧版本 Spring兼容写法
return Result.notFound().message("检查网址是否正确");
return new ResponseEntity<>(Result.notFound().message("检查网址是否正确"), HttpStatus.BAD_REQUEST);
}
@ResponseBody

View File

@@ -29,11 +29,12 @@ public class JwtUtil {
private Long refreshExpire;
/**
* 生成双Token关联设备指纹
*
* @param userId 统一使用user_id
* @param deviceId
* @return
*/
// 生成双Token关联设备指纹
public TokenPair generateTokenPair(String userId, String deviceId) {
String accessToken = Jwts.builder()
.subject(userId)
@@ -64,7 +65,13 @@ public class JwtUtil {
return new TokenPair(accessToken, refreshToken, accessExpire, refreshExpire, deviceId);
}
// 解析Token并返回Claims
/**
* 解析Token并返回Claims
*
* @param token
* @return
*/
public Claims parseToken(String token) {
try {
return Jwts.parser()
@@ -77,7 +84,14 @@ public class JwtUtil {
}
}
// 校验Access Token有效性签名+过期时间+设备绑定)
/**
* 校验Access Token有效性签名+过期时间+设备绑定)
*
* @param userId
* @param accessToken
* @param deviceId
* @return
*/
public boolean validateAccessToken(String userId, String accessToken, String deviceId) {
Claims claims = parseToken(accessToken);
@@ -102,7 +116,14 @@ public class JwtUtil {
return true; // 通过所有校验
}
// 校验Refresh Token有效性签名+Redis一致性
/**
* 校验Refresh Token有效性签名+Redis一致性
*
* @param refreshToken
* @param username
* @return
*/
public boolean validateRefreshToken(String refreshToken, String username) {
Claims claims = parseToken(refreshToken);
@@ -127,7 +148,14 @@ public class JwtUtil {
return true; // 通过所有校验
}
// 使用Refresh Token刷新Access Token
/**
* 使用Refresh Token刷新Access Token
*
* @param refreshToken
* @param deviceId
* @return
*/
public TokenPair refreshAccessToken(String refreshToken, String deviceId) {
Claims claims = parseToken(refreshToken);
String userId = claims.getSubject();