From 2c4ea1a675d8ada3865ebfc7853686088f2e7a0d Mon Sep 17 00:00:00 2001 From: chen-xin-zhi <3588068430@qq.com> Date: Mon, 28 Apr 2025 12:34:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=97=E5=BC=80=E5=BE=97=E8=83=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/aop/PermissionCheck.java | 35 ++++++--- .../config/Knife4jOperationCustomizer.java | 29 +++++++ .../controller/user/UserInfoController.java | 76 ++++++++++++++----- .../exception/GlobalExceptionHandler.java | 5 +- .../model/dto/user/UserInfoAddRequest.java | 22 +++++- .../model/dto/user/UserInfoLoginRequest.java | 27 +++++++ .../model/dto/user/UserInfoQueryRequest.java | 44 +---------- .../model/dto/user/UserInfoUpdateRequest.java | 15 +++- .../promotion/model/entity/UserInfo.java | 7 +- .../service/user/UserInfoService.java | 7 ++ .../user/impl/UserInfoServiceImpl.java | 45 ++++++++++- .../greenorange/promotion/utils/JWTUtils.java | 31 ++++++-- .../com/greenorange/promotion/JwtDemo.java | 1 + 13 files changed, 255 insertions(+), 89 deletions(-) create mode 100644 src/main/java/com/greenorange/promotion/config/Knife4jOperationCustomizer.java create mode 100644 src/main/java/com/greenorange/promotion/model/dto/user/UserInfoLoginRequest.java diff --git a/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java b/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java index 8013375..66ff732 100644 --- a/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java +++ b/src/main/java/com/greenorange/promotion/aop/PermissionCheck.java @@ -1,7 +1,11 @@ package com.greenorange.promotion.aop; +import cn.hutool.core.date.DateUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.greenorange.promotion.annotation.RequiresPermission; import com.greenorange.promotion.common.ErrorCode; @@ -9,9 +13,11 @@ import com.greenorange.promotion.exception.ThrowUtils; import com.greenorange.promotion.model.entity.UserInfo; import com.greenorange.promotion.model.enums.UserRoleEnum; import com.greenorange.promotion.service.user.UserInfoService; +import com.greenorange.promotion.utils.JWTUtils; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.User; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -19,6 +25,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.util.Date; import java.util.Objects; @@ -36,6 +43,9 @@ public class PermissionCheck { private UserInfoService userInfoService; + @Resource + private JWTUtils jwtUtils; + /*** * 执行拦截 @@ -50,16 +60,21 @@ public class PermissionCheck { UserRoleEnum mustUserRoleEnum = UserRoleEnum.getEnumByValues(mustRole); ThrowUtils.throwIf(mustUserRoleEnum == null, ErrorCode.NO_AUTH_ERROR); // 获取用户权限 - String token = request.getHeader("token"); - ThrowUtils.throwIf(StringUtils.isBlank(token), ErrorCode.NOT_LOGIN_ERROR); - String id = null; - try { - id = JWT.decode(token).getAudience().get(0); - } catch (JWTDecodeException jwtDecodeException) { - log.info("JWT已失效"); - } - UserInfo userInfo = userInfoService.getById(id); - ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR); + String token = request.getHeader("Authorization"); + ThrowUtils.throwIf(StringUtils.isBlank(token), ErrorCode.NO_AUTH_ERROR, "JWT为空"); + // 解析token + DecodedJWT decodedJWT = jwtUtils.verify(token); + String userAccount = decodedJWT.getClaim("userAccount").asString(); + String userPassword = decodedJWT.getClaim("userPassword").asString(); + // 打印token的过期时间 + Date expiresAt = decodedJWT.getExpiresAt(); + String formatExpiresAt = DateUtil.format(expiresAt, "yyyy-MM-dd HH:mm:ss"); + log.info("Token过期时间为:" + formatExpiresAt); + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(UserInfo::getUserAccount, userAccount).eq(UserInfo::getUserPassword, userPassword); + UserInfo userInfo = userInfoService.getOne(lambdaQueryWrapper); + ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "用户不存在"); + // 获取用户权限的枚举类 String userRole = userInfo.getUserRole(); UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValues(userRole); diff --git a/src/main/java/com/greenorange/promotion/config/Knife4jOperationCustomizer.java b/src/main/java/com/greenorange/promotion/config/Knife4jOperationCustomizer.java new file mode 100644 index 0000000..6fc2eb3 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/config/Knife4jOperationCustomizer.java @@ -0,0 +1,29 @@ +package com.greenorange.promotion.config; + +import com.google.common.net.HttpHeaders; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.GlobalOperationCustomizer; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; + +import java.util.List; + +/** + * @author: cxz + * @description 为每个接口添加鉴权 + */ +@Slf4j +@Component +public class Knife4jOperationCustomizer implements GlobalOperationCustomizer { + @Override + public Operation customize(Operation operation, HandlerMethod handlerMethod) { + List security = operation.getSecurity(); + if (security == null) { + security = List.of(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)); + operation.setSecurity(security); + } + return operation; + } +} \ No newline at end of file diff --git a/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java b/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java index bbe0915..a71c73e 100644 --- a/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java +++ b/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java @@ -1,31 +1,39 @@ package com.greenorange.promotion.controller.user; +import com.auth0.jwt.interfaces.DecodedJWT; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.net.HttpHeaders; +import com.greenorange.promotion.annotation.RequiresPermission; import com.greenorange.promotion.common.BaseResponse; import com.greenorange.promotion.common.ErrorCode; import com.greenorange.promotion.common.ResultUtils; +import com.greenorange.promotion.constant.UserConstant; import com.greenorange.promotion.exception.BusinessException; import com.greenorange.promotion.exception.ThrowUtils; import com.greenorange.promotion.model.dto.CommonBatchRequest; import com.greenorange.promotion.model.dto.CommonRequest; import com.greenorange.promotion.model.dto.user.UserInfoAddRequest; +import com.greenorange.promotion.model.dto.user.UserInfoLoginRequest; import com.greenorange.promotion.model.dto.user.UserInfoQueryRequest; import com.greenorange.promotion.model.dto.user.UserInfoUpdateRequest; import com.greenorange.promotion.model.entity.UserInfo; import com.greenorange.promotion.model.vo.user.UserInfoVO; import com.greenorange.promotion.service.common.CommonService; import com.greenorange.promotion.service.user.UserInfoService; +import com.greenorange.promotion.utils.JWTUtils; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.concurrent.TimeUnit; /** * 用户表 控制器 @@ -40,25 +48,55 @@ public class UserInfoController { @Resource private UserInfoService userInfoService; + @Resource private CommonService commonService; + @Resource + private RedisTemplate redisTemplate; + + + @Resource + private JWTUtils jwtUtils; + + + + + /** + * web端管理员登录 + * @param userInfoLoginRequest 用户登录请求体 + * @return 是否登录成功 + */ + @PostMapping("login") + @Operation(summary = "web端管理员登录", description = "参数:用户登录请求体,权限:管理员(boss, admin),方法名:userInfoLogin") + public BaseResponse userInfoLogin(@RequestBody UserInfoLoginRequest userInfoLoginRequest, HttpServletRequest request) { + String userAccount = userInfoLoginRequest.getUserAccount(); + String userPassword = userInfoLoginRequest.getUserPassword(); + ThrowUtils.throwIf(StringUtils.isAnyBlank(userAccount, userPassword), ErrorCode.PARAMS_ERROR); + String token = userInfoService.userInfoLogin(userAccount, userPassword, request); + return ResultUtils.success(token); + } + + + + /** + * web端管理员退出登录(用户退出时将 token 加入 Redis 黑名单) + * @return 是否退出登录成功 + */ + @PostMapping("logout") + @Operation(summary = "web端管理员退出登录", description = "参数:JWT,权限:管理员(boss, admin),方法名:userInfoLogout") + @RequiresPermission(mustRole = UserConstant.ADMIN_ROLE) + public BaseResponse userInfoLogout(@RequestHeader("Authorization") String token) { + // 获取 token 的过期时间 + DecodedJWT decodedJWT = jwtUtils.verify(token); + long expirationTime = decodedJWT.getExpiresAt().getTime() - System.currentTimeMillis(); + + // 将 token 存入 Redis 黑名单,并设置过期时间与 token 一致 + redisTemplate.opsForValue().set(token, token, expirationTime, TimeUnit.MILLISECONDS); + return ResultUtils.success(true); + } -// /** -// * web端管理员登录 -// * @param userInfoAddRequest 用户表添加请求体 -// * @return 是否添加成功 -// */ -// @PostMapping("add") -// @Operation(summary = "web端管理员添加用户表", description = "参数:用户表添加请求体,权限:管理员(boss, admin),方法名:addUserInfo") -// @AuthCheck(mustRole = UserConstant.ADMIN_ROLE) -// public BaseResponse addUserInfo(@RequestBody UserInfoAddRequest userInfoAddRequest) { -// ThrowUtils.throwIf(userInfoAddRequest == null, ErrorCode.PARAMS_ERROR); -// UserInfo userInfo = commonService.copyProperties(userInfoAddRequest, UserInfo.class); -// userInfoService.save(userInfo); -// return ResultUtils.success(true); -// } @@ -76,6 +114,7 @@ public class UserInfoController { return ResultUtils.success(true); } + /** * web端管理员更新用户表 * @param userInfoUpdateRequest 用户表更新请求体 @@ -135,6 +174,7 @@ public class UserInfoController { */ @PostMapping("queryById") @Operation(summary = "web端管理员根据id查询用户表", description = "参数:用户表查询请求体,权限:管理员(boss, admin),方法名:queryUserInfoById") + @RequiresPermission(mustRole = UserConstant.ADMIN_ROLE) public BaseResponse queryUserInfoById(@RequestBody CommonRequest commonRequest) { ThrowUtils.throwIf(commonRequest == null || commonRequest.getId() <= 0, ErrorCode.PARAMS_ERROR); Long id = commonRequest.getId(); diff --git a/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java b/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java index 65a5eee..7a707b6 100644 --- a/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/greenorange/promotion/exception/GlobalExceptionHandler.java @@ -6,6 +6,7 @@ import com.greenorange.promotion.common.ResultUtils; import io.swagger.v3.oas.annotations.Hidden; import lombok.extern.slf4j.Slf4j; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -23,14 +24,14 @@ public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public BaseResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error("MethodArgumentNotValidException", e); - return ResultUtils.error(ErrorCode.PARAMS_ERROR, "参数验证失败"); + return ResultUtils.error(ErrorCode.PARAMS_ERROR, e.getMessage()); } // 处理消息体解析失败的异常 @ExceptionHandler(HttpMessageNotReadableException.class) public BaseResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { log.error("HttpMessageNotReadableException", e); - return ResultUtils.error(ErrorCode.PARAMS_ERROR, "请求参数不正确"); + return ResultUtils.error(ErrorCode.PARAMS_ERROR, e.getMessage()); } diff --git a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoAddRequest.java b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoAddRequest.java index 7ee2a9e..fc0c0eb 100644 --- a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoAddRequest.java +++ b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoAddRequest.java @@ -3,19 +3,23 @@ package com.greenorange.promotion.model.dto.user; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import java.io.Serial; import java.io.Serializable; /** - * 用户表添加请求体 + * 用户添加请求体 */ @Data -@Schema(description = "用户表添加请求体", requiredProperties = {"name", "categoryId", "price", "image", "period", "isShelves"}) +@Schema(description = "用户表添加请求体", requiredProperties = {"nickName", "userAvatar", "phoneNumber", + "userAccount", "userPassword", "invitationCode", "userRole", "parentUserId", "superUserList"}) public class UserInfoAddRequest implements Serializable { /** * 用户昵称 */ + @NotBlank(message = "参数不能为空") @Schema(description = "用户昵称", example = "${field.example}") private String nickName; @@ -28,13 +32,22 @@ public class UserInfoAddRequest implements Serializable { /** * 手机号 */ + @NotBlank(message = "参数不能为空") @Schema(description = "手机号", example = "${field.example}") private String phoneNumber; /** - * 密码(建议加密存储) + * 账号 */ - @Schema(description = "密码(建议加密存储)", example = "${field.example}") + @NotBlank(message = "参数不能为空") + @Schema(description = "账号", example = "${field.example}") + private String userAccount; + + /** + * 密码 + */ + @NotBlank(message = "参数不能为空") + @Schema(description = "密码", example = "${field.example}") private String userPassword; /** @@ -46,6 +59,7 @@ public class UserInfoAddRequest implements Serializable { /** * 用户角色 */ + @NotBlank(message = "参数不能为空") @Schema(description = "用户角色", example = "${field.example}") private String userRole; diff --git a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoLoginRequest.java b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoLoginRequest.java new file mode 100644 index 0000000..cf1347a --- /dev/null +++ b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoLoginRequest.java @@ -0,0 +1,27 @@ +package com.greenorange.promotion.model.dto.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 用户登录请求体 + */ +@Data +@Schema(description = "用户登录请求体", requiredProperties = {"userAccount", "userPassword"}) +public class UserInfoLoginRequest implements Serializable { + + /** + * 账号 + */ + @Schema(description = "账号", example = "${field.example}") + private String userAccount; + + /** + * 密码 + */ + @Schema(description = "密码", example = "${field.example}") + private String userPassword; + +} diff --git a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoQueryRequest.java b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoQueryRequest.java index 919709d..0caf2d1 100644 --- a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoQueryRequest.java +++ b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoQueryRequest.java @@ -7,7 +7,7 @@ import java.io.Serializable; import com.greenorange.promotion.common.PageRequest; /** - * 用户表查询请求体,继承自分页请求 PageRequest + * 用户查询请求体,继承自分页请求 PageRequest */ @Data @Schema(description = "用户表查询请求体", requiredProperties = {"current", "pageSize"}) @@ -19,54 +19,12 @@ public class UserInfoQueryRequest extends PageRequest implements Serializable { @Schema(description = "用户表 ID", example = "1") private Long id; - /** - * 用户昵称 - */ - @Schema(description = "用户昵称", example = "${field.example}") - private String nickName; - - /** - * 用户头像URL - */ - @Schema(description = "用户头像URL", example = "${field.example}") - private String userAvatar; - /** * 手机号 */ @Schema(description = "手机号", example = "${field.example}") private String phoneNumber; - /** - * 密码(建议加密存储) - */ - @Schema(description = "密码(建议加密存储)", example = "${field.example}") - private String userPassword; - - /** - * 邀请码 - */ - @Schema(description = "邀请码", example = "${field.example}") - private String invitationCode; - - /** - * 用户角色 - */ - @Schema(description = "用户角色", example = "${field.example}") - private String userRole; - - /** - * 上级用户id - */ - @Schema(description = "上级用户id", example = "${field.example}") - private Long parentUserId; - - /** - * 上级用户列表(1,2,3) - */ - @Schema(description = "上级用户列表(1,2,3)", example = "${field.example}") - private String superUserList; - @Serial private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoUpdateRequest.java b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoUpdateRequest.java index 02e810b..fdefe4b 100644 --- a/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoUpdateRequest.java +++ b/src/main/java/com/greenorange/promotion/model/dto/user/UserInfoUpdateRequest.java @@ -7,10 +7,11 @@ import java.io.Serial; import java.io.Serializable; /** - * 用户表更新请求体 + * 用户更新请求体 */ @Data -@Schema(description = "用户表更新请求体", requiredProperties = {"id", "name", "categoryId", "price", "image", "period", "isShelves"}) +@Schema(description = "用户表更新请求体", requiredProperties = {"id", "nickName", "userAvatar", "phoneNumber", + "userAccount", "userPassword", "invitationCode", "userRole", "parentUserId", "superUserList"}) public class UserInfoUpdateRequest implements Serializable { /** @@ -38,9 +39,15 @@ public class UserInfoUpdateRequest implements Serializable { private String phoneNumber; /** - * 密码(建议加密存储) + * 账号 */ - @Schema(description = "密码(建议加密存储)", example = "${field.example}") + @Schema(description = "账号", example = "${field.example}") + private String userAccount; + + /** + * 密码 + */ + @Schema(description = "密码", example = "${field.example}") private String userPassword; /** diff --git a/src/main/java/com/greenorange/promotion/model/entity/UserInfo.java b/src/main/java/com/greenorange/promotion/model/entity/UserInfo.java index a51924d..e27b17f 100644 --- a/src/main/java/com/greenorange/promotion/model/entity/UserInfo.java +++ b/src/main/java/com/greenorange/promotion/model/entity/UserInfo.java @@ -37,7 +37,12 @@ public class UserInfo implements Serializable { private String phoneNumber; /** - * 密码(建议加密存储) + * 账号 + */ + private String userAccount; + + /** + * 密码 */ private String userPassword; diff --git a/src/main/java/com/greenorange/promotion/service/user/UserInfoService.java b/src/main/java/com/greenorange/promotion/service/user/UserInfoService.java index 0f5a9c6..4393fd3 100644 --- a/src/main/java/com/greenorange/promotion/service/user/UserInfoService.java +++ b/src/main/java/com/greenorange/promotion/service/user/UserInfoService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.greenorange.promotion.model.dto.user.UserInfoQueryRequest; import com.greenorange.promotion.model.entity.UserInfo; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.servlet.http.HttpServletRequest; /** * @author 35880 @@ -17,4 +18,10 @@ public interface UserInfoService extends IService { * 获取查询条件 */ QueryWrapper getQueryWrapper(UserInfoQueryRequest userInfoQueryRequest); + + + /** + * web端用户登录 + */ + String userInfoLogin(String userAccount, String userPassword, HttpServletRequest request); } diff --git a/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java b/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java index bc4df9a..f591ef8 100644 --- a/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java +++ b/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java @@ -1,13 +1,27 @@ package com.greenorange.promotion.service.user.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.greenorange.promotion.annotation.RequiresPermission; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.constant.CommonConstant; +import com.greenorange.promotion.exception.ThrowUtils; import com.greenorange.promotion.mapper.UserInfoMapper; import com.greenorange.promotion.model.dto.user.UserInfoQueryRequest; import com.greenorange.promotion.model.entity.UserInfo; import com.greenorange.promotion.service.user.UserInfoService; +import com.greenorange.promotion.utils.JWTUtils; +import com.greenorange.promotion.utils.SqlUtils; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.Map; + /** * @author 35880 * @description 针对表【user_info(用户基本信息表)】的数据库操作Service实现 @@ -17,12 +31,41 @@ import org.springframework.stereotype.Service; public class UserInfoServiceImpl extends ServiceImpl implements UserInfoService{ + @Resource + private JWTUtils jwtUtils; + + /** * 获取查询条件 */ @Override public QueryWrapper getQueryWrapper(UserInfoQueryRequest userInfoQueryRequest) { - return null; + Long id = userInfoQueryRequest.getId(); + String phoneNumber = userInfoQueryRequest.getPhoneNumber(); + String sortField = userInfoQueryRequest.getSortField(); + String sortOrder = userInfoQueryRequest.getSortOrder(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq(id != null, "id", id); + queryWrapper.eq(StringUtils.isNotBlank(phoneNumber), "phoneNumber", phoneNumber); + queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField); + return queryWrapper; + } + + + /** + * web端用户登录 + */ + @Override + public String userInfoLogin(String userAccount, String userPassword, HttpServletRequest request) { + ThrowUtils.throwIf(userAccount.length() < 6 || userPassword.length() < 6, ErrorCode.PARAMS_ERROR); + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(UserInfo::getUserAccount, userAccount).eq(UserInfo::getUserPassword, userPassword); + UserInfo userInfo = this.getOne(lambdaQueryWrapper); + ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "用户不存在"); + Map payload = new HashMap<>(); + payload.put("userAccount", userAccount); + payload.put("userPassword", userPassword); + return jwtUtils.generateToken(payload); } } diff --git a/src/main/java/com/greenorange/promotion/utils/JWTUtils.java b/src/main/java/com/greenorange/promotion/utils/JWTUtils.java index a252293..bcbb3f1 100644 --- a/src/main/java/com/greenorange/promotion/utils/JWTUtils.java +++ b/src/main/java/com/greenorange/promotion/utils/JWTUtils.java @@ -4,18 +4,32 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.exception.ThrowUtils; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; import java.util.Calendar; import java.util.Map; + +@Component public class JWTUtils { - /** - * 生成token header.payload.signature - */ + private static final String SECRET = "qingcheng"; - public static String getToken(Map map) { + + @Resource + private RedisTemplate redisTemplate; + + + /** + * 生成JWT token + */ + public String generateToken(Map map) { Calendar instance = Calendar.getInstance(); // 默认7天过期 @@ -26,14 +40,19 @@ public class JWTUtils { // payload map.forEach(builder::withClaim); + return builder.withExpiresAt(instance.getTime()) //指定令牌过期时间 .sign(Algorithm.HMAC256(SECRET)); } + /** - * 验证token 合法性 + * 解析JWT token */ - public static DecodedJWT verify(String token) { + public DecodedJWT verify(String token) { + // 检查 token 是否在黑名单中 + String tokenFromBlacklist = redisTemplate.opsForValue().get(token); + ThrowUtils.throwIf(tokenFromBlacklist != null, ErrorCode.NO_AUTH_ERROR, "JWT 已被注销"); return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token); } } diff --git a/src/test/java/com/greenorange/promotion/JwtDemo.java b/src/test/java/com/greenorange/promotion/JwtDemo.java index 9b11897..22ff594 100644 --- a/src/test/java/com/greenorange/promotion/JwtDemo.java +++ b/src/test/java/com/greenorange/promotion/JwtDemo.java @@ -8,6 +8,7 @@ import com.auth0.jwt.interfaces.JWTVerifier; import org.junit.jupiter.api.Test; import java.util.Calendar; +import java.util.Date; import java.util.HashMap; public class JwtDemo {