From d2e479b9f83c33b324d05058d147df11fa097536 Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Wed, 6 Aug 2025 11:59:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E5=A4=87=E8=BF=81=E7=A7=BB=E5=8F=AAwe?= =?UTF-8?q?bflux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 19 +++-- .../videotablet/config/SecurityConfig.java | 1 + .../controller/LoginController.java | 64 ++------------ .../controller/UserPasswordController.java | 83 +++++++++++++++++++ .../onekeycall/videotablet/entity/User.java | 4 +- .../handler/GlobalExceptionHandler.java | 15 +++- .../onekeycall/videotablet/utils/JwtUtil.java | 40 +++++++-- 7 files changed, 153 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/onekeycall/videotablet/controller/UserPasswordController.java diff --git a/pom.xml b/pom.xml index 84953e0..7abf7f3 100644 --- a/pom.xml +++ b/pom.xml @@ -35,16 +35,26 @@ 21 + 2025.0.0 - - org.springframework.boot - spring-boot-starter-data-redis-reactive - + + + + + + + + + org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-data-redis-reactive + org.springframework.boot spring-boot-starter-jdbc @@ -148,7 +158,6 @@ 3.1.499 - diff --git a/src/main/java/com/onekeycall/videotablet/config/SecurityConfig.java b/src/main/java/com/onekeycall/videotablet/config/SecurityConfig.java index 84df6ac..b535a90 100644 --- a/src/main/java/com/onekeycall/videotablet/config/SecurityConfig.java +++ b/src/main/java/com/onekeycall/videotablet/config/SecurityConfig.java @@ -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(); diff --git a/src/main/java/com/onekeycall/videotablet/controller/LoginController.java b/src/main/java/com/onekeycall/videotablet/controller/LoginController.java index c0b06ee..8b26b7e 100644 --- a/src/main/java/com/onekeycall/videotablet/controller/LoginController.java +++ b/src/main/java/com/onekeycall/videotablet/controller/LoginController.java @@ -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"); - } - } } \ No newline at end of file diff --git a/src/main/java/com/onekeycall/videotablet/controller/UserPasswordController.java b/src/main/java/com/onekeycall/videotablet/controller/UserPasswordController.java new file mode 100644 index 0000000..7d788dc --- /dev/null +++ b/src/main/java/com/onekeycall/videotablet/controller/UserPasswordController.java @@ -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 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"); + } + } +} diff --git a/src/main/java/com/onekeycall/videotablet/entity/User.java b/src/main/java/com/onekeycall/videotablet/entity/User.java index a407caf..b70702c 100644 --- a/src/main/java/com/onekeycall/videotablet/entity/User.java +++ b/src/main/java/com/onekeycall/videotablet/entity/User.java @@ -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 diff --git a/src/main/java/com/onekeycall/videotablet/handler/GlobalExceptionHandler.java b/src/main/java/com/onekeycall/videotablet/handler/GlobalExceptionHandler.java index 61855e7..2572cd2 100644 --- a/src/main/java/com/onekeycall/videotablet/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/onekeycall/videotablet/handler/GlobalExceptionHandler.java @@ -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> 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 diff --git a/src/main/java/com/onekeycall/videotablet/utils/JwtUtil.java b/src/main/java/com/onekeycall/videotablet/utils/JwtUtil.java index dba9a4a..06c9992 100644 --- a/src/main/java/com/onekeycall/videotablet/utils/JwtUtil.java +++ b/src/main/java/com/onekeycall/videotablet/utils/JwtUtil.java @@ -29,11 +29,12 @@ public class JwtUtil { private Long refreshExpire; /** - * @param userId 统一使用user_id + * 生成双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();