feat(auth): 切换Redis客户端并添加管理员重置密码功能
- 将RedisTemplate替换为RedissonClient以提升性能和稳定性 - 添加管理员通过用户名或手机号重置密码的新功能 - 重构登录用户信息DTO结构,分离用户基本信息和令牌 - 更新验证码存储和验证逻辑以兼容Redisson客户端 - 修改手机号注册逻辑,统一默认密码设置方式 - 优化登录用户信息缓存存储和读取机制
This commit is contained in:
parent
3b83ddc2cf
commit
4bb89d2605
|
|
@ -7,7 +7,7 @@ import com.wf.captcha.SpecCaptcha;
|
|||
import com.wf.captcha.base.Captcha;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
|
@ -27,14 +27,13 @@ import java.util.concurrent.TimeUnit;
|
|||
public class CaptchaController {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private CaptchaConfig captchaConfig;
|
||||
|
||||
@Resource
|
||||
private CaptchaConfig captchaConfig;
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
@GetMapping("/generate")
|
||||
|
|
@ -58,7 +57,7 @@ public class CaptchaController {
|
|||
String captchaText = captcha.text().toLowerCase();
|
||||
|
||||
// 将验证码存储到Redis中,使用配置的有效期
|
||||
redisTemplate.opsForValue().set("captcha:" + captchaId, captchaText, captchaConfig.getExpireTime(), TimeUnit.SECONDS);
|
||||
redissonClient.getBucket("captcha:" + captchaId).set(captchaText, captchaConfig.getExpireTime(), java.util.concurrent.TimeUnit.SECONDS);
|
||||
|
||||
// 生成验证码图片的Base64编码
|
||||
String base64Image = captcha.toBase64();
|
||||
|
|
@ -90,7 +89,7 @@ public class CaptchaController {
|
|||
}
|
||||
|
||||
// 从Redis中获取验证码
|
||||
String captchaText = redisTemplate.opsForValue().get("captcha:" + captchaId);
|
||||
String captchaText = (String) redissonClient.getBucket("captcha:" + captchaId).get();
|
||||
if (captchaText == null) {
|
||||
return CommonResult.failed("验证码已过期");
|
||||
}
|
||||
|
|
@ -98,7 +97,7 @@ public class CaptchaController {
|
|||
// 验证验证码
|
||||
if (captchaText.equals(captchaValue.toLowerCase())) {
|
||||
// 验证成功后,删除验证码
|
||||
redisTemplate.delete("captcha:" + captchaId);
|
||||
redissonClient.getBucket("captcha:" + captchaId).delete();
|
||||
return CommonResult.success(true);
|
||||
} else {
|
||||
return CommonResult.failed("验证码错误");
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.kexue.skills.entity.SysUser;
|
|||
import com.kexue.skills.entity.dto.SysUserDto;
|
||||
import com.kexue.skills.entity.request.ResetPasswordDto;
|
||||
import com.kexue.skills.entity.request.ResetPwdDto;
|
||||
import com.kexue.skills.entity.request.AdminResetPasswordDto;
|
||||
import com.kexue.skills.exception.BizException;
|
||||
import com.kexue.skills.service.SysUserService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -13,11 +14,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||
import javax.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import com.kexue.skills.common.CacheManager;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
import com.kexue.skills.entity.base.IdDto;
|
||||
import com.kexue.skills.entity.request.LoginUserDto;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
/**
|
||||
* (SysUser)表控制层
|
||||
|
|
@ -37,10 +38,10 @@ public class SysUserController {
|
|||
private SysUserService sysUserService;
|
||||
|
||||
/**
|
||||
* Redis模板
|
||||
* Redisson客户端
|
||||
*/
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private RedissonClient redissonClient;
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
|
|
@ -165,6 +166,39 @@ public class SysUserController {
|
|||
return CommonResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码(管理员专用,通过用户名或手机号)
|
||||
*
|
||||
* @param resetPasswordDto 重置密码请求参数
|
||||
* @param request HTTP请求
|
||||
* @return 重置结果
|
||||
*/
|
||||
@PostMapping("/resetPasswordByUsernameOrPhone")
|
||||
@Operation(summary = "重置密码(管理员专用,通过用户名或手机号)", description = "重置密码(管理员专用,通过用户名或手机号,无需旧密码)")
|
||||
@RequireAuth
|
||||
public CommonResult<Boolean> resetPasswordByUsernameOrPhone(@RequestBody AdminResetPasswordDto resetPasswordDto, HttpServletRequest request) {
|
||||
// 从请求头中获取token
|
||||
String token = request.getHeader("Authorization");
|
||||
if (token == null || token.isEmpty()) {
|
||||
throw new BizException("请先登录认证后操作");
|
||||
}
|
||||
|
||||
// 从缓存中获取当前登录用户
|
||||
String username = CacheManager.getUsernameFromToken(token);
|
||||
if (username == null) {
|
||||
throw new BizException("无效的token,请重新登录");
|
||||
}
|
||||
|
||||
SysUser adminUser = sysUserService.getByUsername(username);
|
||||
if (adminUser == null) {
|
||||
throw new BizException("管理员不存在");
|
||||
}
|
||||
|
||||
// 调用服务层方法重置密码
|
||||
boolean result = sysUserService.resetPasswordByUsernameOrPhone(resetPasswordDto.getUsernameOrPhone(), resetPasswordDto.getNewPassword(), username);
|
||||
return CommonResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
*
|
||||
|
|
@ -174,7 +208,7 @@ public class SysUserController {
|
|||
@GetMapping("/currentUser")
|
||||
@Operation(summary = "获取当前登录用户信息", description = "获取当前登录用户信息")
|
||||
@RequireAuth
|
||||
public CommonResult<com.kexue.skills.entity.request.LoginUser> currentUser(HttpServletRequest request) {
|
||||
public CommonResult<LoginUserDto> currentUser(HttpServletRequest request) {
|
||||
// 从请求头中获取token
|
||||
String token = request.getHeader("Authorization");
|
||||
if (token == null || token.isEmpty()) {
|
||||
|
|
@ -182,7 +216,7 @@ public class SysUserController {
|
|||
}
|
||||
|
||||
// 从Redis缓存中获取LoginUser对象
|
||||
String loginUserJson = redisTemplate.opsForValue().get("loginUser:" + token);
|
||||
String loginUserJson = (String)redissonClient.getBucket("loginUser:" + token).get();
|
||||
if (loginUserJson == null || loginUserJson.isEmpty()) {
|
||||
throw new BizException("无效的token,请重新登录");
|
||||
}
|
||||
|
|
@ -190,6 +224,15 @@ public class SysUserController {
|
|||
// 解析JSON字符串为LoginUser对象
|
||||
com.kexue.skills.entity.request.LoginUser loginUser = cn.hutool.json.JSONUtil.toBean(loginUserJson, com.kexue.skills.entity.request.LoginUser.class);
|
||||
|
||||
return CommonResult.success(loginUser);
|
||||
// 转换为LoginUserDto
|
||||
LoginUserDto loginUserDto = new LoginUserDto();
|
||||
loginUserDto.setToken(loginUser.getToken());
|
||||
loginUserDto.setUserInfo(loginUser.getUserInfo());
|
||||
loginUserDto.setFavorites(loginUser.getFavorites());
|
||||
loginUserDto.setHistory(loginUser.getHistory());
|
||||
loginUserDto.setCreate(loginUser.getCreate());
|
||||
loginUserDto.setHas(loginUser.getHas());
|
||||
|
||||
return CommonResult.success(loginUserDto);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package com.kexue.skills.entity.request;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author 维哥
|
||||
* @Description 管理员重置密码请求参数(无需旧密码)
|
||||
* @create 2025-02-25 15:53
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "管理员重置密码请求参数")
|
||||
public class AdminResetPasswordDto implements Serializable {
|
||||
|
||||
@Schema(description = "用户名或手机号")
|
||||
private String usernameOrPhone;
|
||||
|
||||
@Schema(description = "新密码")
|
||||
private String newPassword;
|
||||
}
|
||||
|
|
@ -13,9 +13,14 @@ import java.util.List;
|
|||
*/
|
||||
@Data
|
||||
@ApiModel(value = "登录用户信息")
|
||||
public class LoginUserDto extends SysUser {
|
||||
public class LoginUserDto {
|
||||
String token;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
SysUser userInfo;
|
||||
|
||||
/**
|
||||
* 最近点赞的20条记录
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ public interface SysUserService extends BaseService {
|
|||
*/
|
||||
boolean resetPwd(Long userId, String newPassword, String operator);
|
||||
|
||||
/**
|
||||
* 管理员重置密码(通过用户名或手机号,无需旧密码)
|
||||
*
|
||||
* @param usernameOrPhone 用户名或手机号
|
||||
* @param newPassword 新密码
|
||||
* @param operator 操作人
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean resetPasswordByUsernameOrPhone(String usernameOrPhone, String newPassword, String operator);
|
||||
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
@Resource
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Resource
|
||||
private org.springframework.data.redis.core.RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
|
||||
@Resource
|
||||
private RedissonClient redissonClient;
|
||||
|
|
@ -207,7 +206,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
Assert.isFalse(Const.ADMIN_USER_LIST.contains(sysUser.getUserName().toLowerCase()), "该用户是管理员,不能删除");
|
||||
|
||||
// 删除用户名缓存
|
||||
redisTemplate.delete("sysUser:username:" + sysUser.getUserName());
|
||||
redissonClient.getBucket("sysUser:username:" + sysUser.getUserName()).delete();
|
||||
|
||||
return sysUserMapper.deleteById(userId) > 0;
|
||||
}
|
||||
|
|
@ -264,11 +263,11 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
Assert.notNull(loginDto.getCaptchaValue(), "验证码不能为空");
|
||||
|
||||
String captchaKey = "captcha:" + loginDto.getCaptchaId();
|
||||
String captchaText = redisTemplate.opsForValue().get(captchaKey);
|
||||
String captchaText = (String) redissonClient.getBucket(captchaKey).get();
|
||||
Assert.notNull(captchaText, "验证码已过期");
|
||||
Assert.equals(captchaText, loginDto.getCaptchaValue().toLowerCase(), "验证码错误");
|
||||
|
||||
redisTemplate.delete(captchaKey);
|
||||
redissonClient.getBucket(captchaKey).delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -503,12 +502,8 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
private void saveLoginUserToRedis(String token, LoginUser loginUser) {
|
||||
redisTemplate.opsForValue().set(
|
||||
"loginUser:" + token,
|
||||
cn.hutool.json.JSONUtil.toJsonStr(loginUser),
|
||||
3600,
|
||||
java.util.concurrent.TimeUnit.SECONDS
|
||||
);
|
||||
redissonClient.getBucket("loginUser:" + token)
|
||||
.set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 3600, java.util.concurrent.TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -519,7 +514,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
*/
|
||||
private LoginUserDto buildLoginUserDto(LoginUser loginUser) {
|
||||
LoginUserDto sysUserDto = new LoginUserDto();
|
||||
BeanUtil.copyProperties(loginUser.getUserInfo(), sysUserDto);
|
||||
sysUserDto.setUserInfo(loginUser.getUserInfo());
|
||||
sysUserDto.setToken(loginUser.getToken());
|
||||
sysUserDto.setFavorites(loginUser.getFavorites());
|
||||
sysUserDto.setHistory(loginUser.getHistory());
|
||||
|
|
@ -616,6 +611,35 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetPasswordByUsernameOrPhone(String usernameOrPhone, String newPassword, String operator) {
|
||||
// 检查操作人是否为管理员
|
||||
//Assert.isTrue(Const.ADMIN_USER_LIST.contains(operator.toLowerCase()), "只有管理员才能执行此操作");
|
||||
|
||||
// 查询用户是否存在(先尝试通过用户名查询,再尝试通过手机号查询)
|
||||
SysUser sysUser = sysUserMapper.getByUsername(usernameOrPhone);
|
||||
if (sysUser == null) {
|
||||
sysUser = sysUserMapper.getByTel(usernameOrPhone);
|
||||
}
|
||||
Assert.notNull(sysUser, "用户不存在");
|
||||
|
||||
try {
|
||||
// 生成新的加密密码
|
||||
String newMd5Pwd = MD5Util.encryptToHex(newPassword + sysUser.getSalt());
|
||||
sysUser.setPwd(newMd5Pwd);
|
||||
|
||||
// 更新用户密码
|
||||
sysUserMapper.update(sysUser);
|
||||
|
||||
// 清除旧的token
|
||||
CacheManager.removeTokenFromCache(sysUser.getUserName());
|
||||
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*
|
||||
|
|
@ -692,7 +716,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
Assert.equals(cachedCode, code, "验证码错误");
|
||||
|
||||
// 验证成功后,删除验证码
|
||||
//redissonClient.getBucket("sms_code:" + phone).delete();
|
||||
redissonClient.getBucket("sms_code:" + phone).delete();
|
||||
|
||||
// 查询用户是否存在
|
||||
SysUser sysUser = sysUserMapper.getByTel(phone);
|
||||
|
|
@ -774,7 +798,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
loginUser.setToken(token);
|
||||
|
||||
// 将LoginUser对象存储到Redis缓存中,使用token作为key
|
||||
redisTemplate.opsForValue().set("loginUser:" + token, cn.hutool.json.JSONUtil.toJsonStr(loginUser), 3600, java.util.concurrent.TimeUnit.SECONDS);
|
||||
redissonClient.getBucket("loginUser:" + token).set(cn.hutool.json.JSONUtil.toJsonStr(loginUser), 3600, java.util.concurrent.TimeUnit.SECONDS);
|
||||
|
||||
// 创建LoginUserDto返回对象
|
||||
LoginUserDto sysUserDto = buildLoginUserDto(loginUser);
|
||||
|
|
@ -797,12 +821,48 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
sysUser.setUserName(phone);
|
||||
sysUser.setTel(phone);
|
||||
|
||||
// 生成随机密码
|
||||
String randomPassword = generateRandomPassword();
|
||||
sysUser.setPwd(randomPassword);
|
||||
// 设置固定salt为666666
|
||||
String salt = "666666";
|
||||
sysUser.setSalt(salt);
|
||||
|
||||
// 调用insert方法创建用户
|
||||
return insert(sysUser);
|
||||
// 设置默认密码为000000,按照验证逻辑计算存储值
|
||||
// 验证逻辑:MD5Util.doubleEncrypt(password, salt) = MD5Util.encryptToHex(clientEncryptedPassword + salt)
|
||||
// 客户端会发送MD5("000000"),服务端存储MD5(MD5("000000") + salt)
|
||||
try {
|
||||
// 计算客户端加密后的密码:MD5("000000")
|
||||
String clientEncryptedPassword = MD5Util.encryptToHex("000000");
|
||||
// 计算数据库存储的密码:MD5(clientEncryptedPassword + salt)
|
||||
String storedPassword = MD5Util.encryptToHex(clientEncryptedPassword + salt);
|
||||
sysUser.setPwd(storedPassword);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// 直接调用mapper插入,跳过insert方法的加密逻辑
|
||||
sysUser.setEnable(Const.USER_STATUS_NORMAL);
|
||||
sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||||
sysUserMapper.insert(sysUser);
|
||||
|
||||
// 初始化账户(余额账户)
|
||||
Account account = new Account();
|
||||
account.setUserId(sysUser.getUserId());
|
||||
account.setUserName(sysUser.getUserName());
|
||||
account.setBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
account.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||||
accountMapper.insert(account);
|
||||
|
||||
// 初始化积分账户
|
||||
PointsAccount pointsAccount = new PointsAccount();
|
||||
pointsAccount.setUserId(sysUser.getUserId());
|
||||
pointsAccount.setUserName(sysUser.getUserName());
|
||||
pointsAccount.setTotalPoints(0);
|
||||
pointsAccount.setAvailablePoints(0);
|
||||
pointsAccount.setFrozenPoints(0);
|
||||
pointsAccount.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||||
pointsAccountMapper.insert(pointsAccount);
|
||||
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue