From 2b4dcdb375f96afca35ca05e80771a598365d9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sat, 4 Apr 2026 10:53:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=88=A0=E9=99=A4=E6=89=AB?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 QrLoginController 控制器类及其所有接口方法 - 删除 QrLoginConfirmRequest、QrLoginData、QrLoginGenerateResponse、QrLoginStatusResponse 数据传输对象 - 删除 QrLoginService 接口及其实现类 QrLoginServiceImpl - 移除扫码登录相关的业务逻辑和 Redis 缓存操作 - 删除微信小程序扫码登录确认功能 - 移除相关的 Swagger 文档注解和验证逻辑 --- .../auto/controller/QrLoginController.java | 104 -------- .../auto/dto/QrLoginConfirmRequest.java | 50 ---- .../com/gxwebsoft/auto/dto/QrLoginData.java | 55 ---- .../auto/dto/QrLoginGenerateResponse.java | 29 --- .../auto/dto/QrLoginStatusResponse.java | 32 --- .../auto/service/QrLoginService.java | 46 ---- .../auto/service/impl/QrLoginServiceImpl.java | 239 ------------------ 7 files changed, 555 deletions(-) delete mode 100644 src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java delete mode 100644 src/main/java/com/gxwebsoft/auto/service/QrLoginService.java delete mode 100644 src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java diff --git a/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java b/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java deleted file mode 100644 index d296d82..0000000 --- a/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.gxwebsoft.auto.controller; - -import com.gxwebsoft.auto.dto.QrLoginConfirmRequest; -import com.gxwebsoft.auto.dto.QrLoginGenerateResponse; -import com.gxwebsoft.auto.dto.QrLoginStatusResponse; -import com.gxwebsoft.auto.service.QrLoginService; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.ApiResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -/** - * 认证模块 - * - * @author 科技小王子 - * @since 2025-03-06 22:50:25 - */ -@Tag(name = "认证模块") -@RestController -@RequestMapping("/api/qr-login") -public class QrLoginController extends BaseController { - - @Autowired - private QrLoginService qrLoginService; - - /** - * 生成扫码登录token - */ - @Operation(summary = "生成扫码登录token") - @PostMapping("/generate") - public ApiResult generateQrLoginToken() { - try { - QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken(); - return success("生成成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 检查扫码登录状态 - */ - @Operation(summary = "检查扫码登录状态") - @GetMapping("/status/{token}") - public ApiResult checkQrLoginStatus( - @Parameter(description = "扫码登录token") @PathVariable String token) { - try { - QrLoginStatusResponse response = qrLoginService.checkQrLoginStatus(token); - return success("查询成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 确认扫码登录 - */ - @Operation(summary = "确认扫码登录") - @PostMapping("/confirm") - public ApiResult confirmQrLogin(@Valid @RequestBody QrLoginConfirmRequest request) { - try { - QrLoginStatusResponse response = qrLoginService.confirmQrLogin(request); - return success("确认成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 扫码操作(可选接口,用于移动端扫码后更新状态) - */ - @Operation(summary = "扫码操作") - @PostMapping("/scan/{token}") - public ApiResult scanQrCode(@Parameter(description = "扫码登录token") @PathVariable String token) { - try { - boolean result = qrLoginService.scanQrCode(token); - return success("操作成功", result); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 微信小程序扫码登录确认(便捷接口) - */ - @Operation(summary = "微信小程序扫码登录确认") - @PostMapping("/wechat-confirm") - public ApiResult wechatMiniProgramConfirm(@Valid @RequestBody QrLoginConfirmRequest request) { - try { - // 设置平台为微信小程序 - request.setPlatform("miniprogram"); - QrLoginStatusResponse response = qrLoginService.confirmQrLogin(request); - return success("微信小程序登录确认成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java deleted file mode 100644 index f3b423e..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -/** - * 扫码登录确认请求 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@Schema(description = "扫码登录确认请求") -public class QrLoginConfirmRequest { - - @Schema(description = "扫码登录token") - @NotBlank(message = "token不能为空") - private String token; - - @Schema(description = "用户ID") - private Integer userId; - - @Schema(description = "登录平台: web-网页端, app-移动应用, miniprogram-微信小程序") - private String platform; - - @Schema(description = "微信小程序相关信息") - private WechatMiniProgramInfo wechatInfo; - - /** - * 微信小程序信息 - */ - @Data - @Schema(description = "微信小程序信息") - public static class WechatMiniProgramInfo { - @Schema(description = "微信openid") - private String openid; - - @Schema(description = "微信unionid") - private String unionid; - - @Schema(description = "微信昵称") - private String nickname; - - @Schema(description = "微信头像") - private String avatar; - } - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java deleted file mode 100644 index 563bf1d..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * 扫码登录数据模型 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class QrLoginData { - - /** - * 扫码登录token - */ - private String token; - - /** - * 状态: pending-等待扫码, scanned-已扫码, confirmed-已确认, expired-已过期 - */ - private String status; - - /** - * 用户ID(扫码确认后设置) - */ - private Integer userId; - - /** - * 用户名(扫码确认后设置) - */ - private String username; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - - /** - * 过期时间 - */ - private LocalDateTime expireTime; - - /** - * JWT访问令牌(确认后生成) - */ - private String accessToken; - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java deleted file mode 100644 index f0b69e5..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 扫码登录生成响应 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Schema(description = "扫码登录生成响应") -public class QrLoginGenerateResponse { - - @Schema(description = "扫码登录token") - private String token; - - @Schema(description = "二维码内容") - private String qrCode; - - @Schema(description = "过期时间(秒)") - private Long expiresIn; - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java deleted file mode 100644 index 1eb0d4a..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 扫码登录状态响应 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Schema(description = "扫码登录状态响应") -public class QrLoginStatusResponse { - - @Schema(description = "状态: pending-等待扫码, scanned-已扫码, confirmed-已确认, expired-已过期") - private String status; - - @Schema(description = "JWT访问令牌(仅在confirmed状态时返回)") - private String accessToken; - - @Schema(description = "用户信息(仅在confirmed状态时返回)") - private Object userInfo; - - @Schema(description = "剩余过期时间(秒)") - private Long expiresIn; - -} diff --git a/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java b/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java deleted file mode 100644 index 85ed28f..0000000 --- a/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gxwebsoft.auto.service; - -import com.gxwebsoft.auto.dto.QrLoginConfirmRequest; -import com.gxwebsoft.auto.dto.QrLoginGenerateResponse; -import com.gxwebsoft.auto.dto.QrLoginStatusResponse; - -/** - * 扫码登录服务接口 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -public interface QrLoginService { - - /** - * 生成扫码登录token - * - * @return QrLoginGenerateResponse - */ - QrLoginGenerateResponse generateQrLoginToken(); - - /** - * 检查扫码登录状态 - * - * @param token 扫码登录token - * @return QrLoginStatusResponse - */ - QrLoginStatusResponse checkQrLoginStatus(String token); - - /** - * 确认扫码登录 - * - * @param request 确认请求 - * @return QrLoginStatusResponse - */ - QrLoginStatusResponse confirmQrLogin(QrLoginConfirmRequest request); - - /** - * 扫码操作(更新状态为已扫码) - * - * @param token 扫码登录token - * @return boolean - */ - boolean scanQrCode(String token); - -} diff --git a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java b/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java deleted file mode 100644 index 34658e8..0000000 --- a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.gxwebsoft.auto.service.impl; - -import cn.hutool.core.lang.UUID; -import cn.hutool.core.util.StrUtil; -import com.gxwebsoft.auto.dto.*; -import com.gxwebsoft.auto.service.QrLoginService; -import com.gxwebsoft.common.core.security.JwtSubject; -import com.gxwebsoft.common.core.security.JwtUtil; -import com.gxwebsoft.common.core.utils.JSONUtil; -import com.gxwebsoft.common.core.utils.RedisUtil; -import com.gxwebsoft.common.system.entity.User; -import com.gxwebsoft.common.system.service.UserService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.concurrent.TimeUnit; - -import static com.gxwebsoft.common.core.constants.RedisConstants.*; - -/** - * 扫码登录服务实现 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Slf4j -@Service -public class QrLoginServiceImpl implements QrLoginService { - - @Autowired - private RedisUtil redisUtil; - - @Autowired - private UserService userService; - - @Value("${config.jwt.secret:websoft-jwt-secret-key-2025}") - private String jwtSecret; - - @Value("${config.jwt.expire:86400}") - private Long jwtExpire; - - @Override - public QrLoginGenerateResponse generateQrLoginToken() { - // 生成唯一的扫码登录token - String token = UUID.randomUUID().toString(true); - - // 创建扫码登录数据 - QrLoginData qrLoginData = new QrLoginData(); - qrLoginData.setToken(token); - qrLoginData.setStatus(QR_LOGIN_STATUS_PENDING); - qrLoginData.setCreateTime(LocalDateTime.now()); - qrLoginData.setExpireTime(LocalDateTime.now().plusSeconds(QR_LOGIN_TOKEN_TTL)); - - // 存储到Redis,设置过期时间 - String redisKey = QR_LOGIN_TOKEN_KEY + token; - redisUtil.set(redisKey, qrLoginData, QR_LOGIN_TOKEN_TTL, TimeUnit.SECONDS); - - log.info("生成扫码登录token: {}", token); - - // 构造二维码内容(这里可以是前端登录页面的URL + token参数) - String qrCodeContent = "qr-login:" + token; - - return new QrLoginGenerateResponse(token, qrCodeContent, QR_LOGIN_TOKEN_TTL); - } - - @Override - public QrLoginStatusResponse checkQrLoginStatus(String token) { - if (StrUtil.isBlank(token)) { - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - // 删除过期的token - redisUtil.delete(redisKey); - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - // 计算剩余过期时间 - long expiresIn = ChronoUnit.SECONDS.between(LocalDateTime.now(), qrLoginData.getExpireTime()); - - QrLoginStatusResponse response = new QrLoginStatusResponse(); - response.setStatus(qrLoginData.getStatus()); - response.setExpiresIn(expiresIn); - - // 如果已确认,返回token和用户信息 - if (QR_LOGIN_STATUS_CONFIRMED.equals(qrLoginData.getStatus())) { - response.setAccessToken(qrLoginData.getAccessToken()); - - // 获取用户信息 - if (qrLoginData.getUserId() != null) { - User user = userService.getByIdRel(qrLoginData.getUserId()); - if (user != null) { - // 清除敏感信息 - user.setPassword(null); - response.setUserInfo(user); - } - } - - // 确认后删除token,防止重复使用 - redisUtil.delete(redisKey); - } - - return response; - } - - @Override - public QrLoginStatusResponse confirmQrLogin(QrLoginConfirmRequest request) { - String token = request.getToken(); - Integer userId = request.getUserId(); - String platform = request.getPlatform(); - - if (StrUtil.isBlank(token) || userId == null) { - throw new RuntimeException("参数不能为空"); - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - throw new RuntimeException("扫码登录token不存在或已过期"); - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - redisUtil.delete(redisKey); - throw new RuntimeException("扫码登录token已过期"); - } - - // 获取用户信息 - User user = userService.getByIdRel(userId); - if (user == null) { - throw new RuntimeException("用户不存在"); - } - - // 检查用户状态 - if (user.getStatus() != null && user.getStatus() != 0) { - throw new RuntimeException("用户已被冻结"); - } - - // 如果是微信小程序登录,处理微信相关信息 - if ("miniprogram".equals(platform) && request.getWechatInfo() != null) { - handleWechatMiniProgramLogin(user, request.getWechatInfo()); - } - - // 生成JWT token - JwtSubject jwtSubject = new JwtSubject(user.getUsername(), user.getTenantId()); - String accessToken = JwtUtil.buildToken(jwtSubject, jwtExpire, jwtSecret); - - // 更新扫码登录数据 - qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED); - qrLoginData.setUserId(userId); - qrLoginData.setUsername(user.getUsername()); - qrLoginData.setAccessToken(accessToken); - - // 更新Redis中的数据 - redisUtil.set(redisKey, qrLoginData, 60L, TimeUnit.SECONDS); // 给前端60秒时间获取token - - log.info("用户 {} 通过 {} 平台确认扫码登录,token: {}", user.getUsername(), - platform != null ? platform : "unknown", token); - - // 清除敏感信息 - user.setPassword(null); - - return new QrLoginStatusResponse(QR_LOGIN_STATUS_CONFIRMED, accessToken, user, 60L); - } - - /** - * 处理微信小程序登录相关逻辑 - */ - private void handleWechatMiniProgramLogin(User user, QrLoginConfirmRequest.WechatMiniProgramInfo wechatInfo) { - // 更新用户的微信信息 - if (StrUtil.isNotBlank(wechatInfo.getOpenid())) { - user.setOpenid(wechatInfo.getOpenid()); - } - if (StrUtil.isNotBlank(wechatInfo.getUnionid())) { - user.setUnionid(wechatInfo.getUnionid()); - } - if (StrUtil.isNotBlank(wechatInfo.getNickname()) && StrUtil.isBlank(user.getNickname())) { - user.setNickname(wechatInfo.getNickname()); - } - if (StrUtil.isNotBlank(wechatInfo.getAvatar()) && StrUtil.isBlank(user.getAvatar())) { - user.setAvatar(wechatInfo.getAvatar()); - } - - // 更新用户信息到数据库 - try { - userService.updateById(user); - log.info("更新用户 {} 的微信小程序信息成功", user.getUsername()); - } catch (Exception e) { - log.warn("更新用户 {} 的微信小程序信息失败: {}", user.getUsername(), e.getMessage()); - } - } - - @Override - public boolean scanQrCode(String token) { - if (StrUtil.isBlank(token)) { - return false; - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - return false; - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - redisUtil.delete(redisKey); - return false; - } - - // 只有pending状态才能更新为scanned - if (QR_LOGIN_STATUS_PENDING.equals(qrLoginData.getStatus())) { - qrLoginData.setStatus(QR_LOGIN_STATUS_SCANNED); - - // 计算剩余过期时间 - long remainingSeconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), qrLoginData.getExpireTime()); - redisUtil.set(redisKey, qrLoginData, remainingSeconds, TimeUnit.SECONDS); - - log.info("扫码登录token {} 状态更新为已扫码", token); - return true; - } - - return false; - } -}