基于Spring Boot的系统用户管理模块实现
简介
在现代企业级应用中,用户管理模块是系统的基础功能之一。它通常包括用户的增删改查、登录认证、权限管理等功能。本文将详细解析一个基于Spring Boot的系统用户管理模块的实现流程。该模块使用了MyBatis Plus作为ORM框架,结合Redis进行验证码的存储与验证,同时通过Sa-Token进行权限控制。
代码结构
项目的主要代码结构如下:
- UserController.java: 用户管理的控制器,负责处理HTTP请求。
- UserService.java: 用户管理的服务接口,定义了用户管理的核心业务逻辑。
- UserServiceImpl.java: 用户管理服务的具体实现类。
- SysUser.java: 系统用户实体类,对应数据库中的用户表。
功能实现解析
1. 用户登录与验证码生成
1.1 登录功能
在UserServiceImpl
类中,login
方法负责处理用户登录请求。登录流程如下:
-
解码请求参数:从
LoginVO
中获取经过Base64编码的用户名、密码、验证码和验证码Token,并进行解码。String username = new String(Base64.getDecoder().decode(loginVO.getIa())); String password = new String(Base64.getDecoder().decode(loginVO.getIp())); String vercode = new String(Base64.getDecoder().decode(loginVO.getVc())); String vercodeToken = new String(Base64.getDecoder().decode(loginVO.getVt()));
-
验证码校验:从Redis中根据验证码Token获取存储的验证码,并与用户输入的验证码进行比对。如果验证码不匹配,抛出
CODE_ERROR
异常。String code = redisTemplate.opsForValue().get(vercodeToken); if (ObjectUtil.isEmpty(code) || !StrUtil.equalsIgnoreCase(code,vercode)){ throw new QLException(ExceptionEnum.CODE_ERROR); }
-
用户信息查询:根据用户名查询用户信息。如果用户不存在,抛出
USER_NOT_NULL
异常。LambdaQueryWrapper<SysUser> userWrapper = new LambdaQueryWrapper<>(); userWrapper.eq(SysUser::getLoginUsername,username); SysUser sysUser = this.getOne(userWrapper); if (ObjectUtil.isEmpty(sysUser)){ throw new QLException(USER_NOT_NULL); }
-
平台类型校验:检查用户所属平台是否与系统类型一致,如果不一致,抛出
SYSTEM_TYPE_ERROR
异常。String sysType = sysUser.getSysType(); if (!ObjectUtil.equal(sysType,SYSTRM_TYPE)){ throw new QLException(SYSTEM_TYPE_ERROR); }
-
密码校验:查询用户的认证信息,获取盐值和加密后的密码。将用户输入的密码与盐值拼接后进行哈希计算,并与数据库中存储的密码进行比对。如果密码不匹配,抛出
USER_NOT_NULL
异常。LambdaQueryWrapper<SysUserAuth> userAuthWrapper = new LambdaQueryWrapper<>(); userAuthWrapper.eq(SysUserAuth::getUserId, sysUserId); SysUserAuth userAuth = userAuthService.getOne(userAuthWrapper); String credential = userAuth.getCredential(); String salt = userAuth.getSalt(); String SaltPassword = password + salt; boolean isTrue = PasswordUtil.verifyPassword(SaltPassword, credential); if (!isTrue){ throw new QLException(USER_NOT_NULL); }
-
返回登录结果:如果所有校验通过,返回
LoginDTO
对象,包含用户的基本信息和登录凭证。LoginDTO loginDTO = new LoginDTO(); loginDTO.setUsername(username); loginDTO.setPassword(password); loginDTO.setVercode(vercode); loginDTO.setVercodeToken(vercodeToken); loginDTO.setUserId(sysUser.getSysUserId()); return loginDTO;
1.2 验证码生成
getVercode
方法负责生成图片验证码,并将验证码存储到Redis中。具体流程如下:
-
生成验证码图片:使用
CaptchaUtil
生成一个包含干扰线的验证码图片,并设置背景颜色为白色。LineCaptcha captcha = CaptchaUtil.createLineCaptcha(120, 40, 4, 5); captcha.setBackground(Color.white);
-
生成验证码Token:使用雪花算法生成一个唯一的Token,作为验证码的标识。
long nextId = idWorker.snowflake().nextId(); String sessionId = CHECK_PREFIX + String.valueOf(nextId);
-
存储验证码:将验证码和Token存储到Redis中,设置过期时间为1分钟。
redisTemplate.opsForValue().set(sessionId , code , 1 , TimeUnit.MINUTES);
-
返回验证码信息:将验证码图片的Base64编码、过期时间和Token封装到
VerCodeDTO
对象中返回。VerCodeDTO verCodeDTO = new VerCodeDTO(); verCodeDTO.setImageBase64Data(fullImageBase64); verCodeDTO.setExpireTime(60); verCodeDTO.setVercodeToken(sessionId); return ResSerialization.success(verCodeDTO);
2. 用户管理功能
2.1 分页查询用户
selectUserByUserIdOrUserName
方法实现了根据用户ID或用户名进行分页查询的功能。具体流程如下:
-
参数校验:检查分页参数是否为空,如果为空则设置默认值。
if (ObjectUtil.isEmpty(stratPage)){ stratPage = 1; } if (ObjectUtil.isEmpty(limit)){ limit = 10; }
-
查询总条数:使用MyBatis Plus的
count
方法查询用户总数。Long count = this.count(null); total = Integer.valueOf(Math.toIntExact(count));
-
计算最大页数:根据总条数和每页大小计算最大页数,如果请求的页数超过最大页数,则设置为最大页数。
Integer maxPage = (int) (total / limit) + 1; if (stratPage > maxPage){ stratPage = maxPage; }
-
查询用户列表:调用
userMapper.selectUserByUserIdOrUserName
方法,根据用户ID或用户名进行模糊查询,并返回分页结果。Integer startSize = (stratPage - 1) * limit; records = userMapper.selectUserByUserIdOrUserName(userId,userName,startSize,limit);
-
返回结果:将查询结果封装到
UserListDTO
对象中返回。UserListDTO userListDTO = new UserListDTO(); userListDTO.setCurrent(current); userListDTO.setTotal(total); userListDTO.setRecords(records); userListDTO.setHasNext(hasNext); return ResSerialization.success(userListDTO);
2.2 删除用户
deleteUser
方法实现了删除用户的功能。具体流程如下:
-
删除用户信息:根据用户ID删除用户表中的记录。
boolean isDelete = this.remove(new LambdaQueryWrapper<SysUser>().eq(SysUser::getSysUserId, userId)); if (!isDelete){ throw new QLException(USER_REMOVE_ERROR); }
-
删除用户认证信息:删除用户认证表中的记录。
LambdaQueryWrapper<SysUserAuth> userAuthLambdaQueryWrapper = new LambdaQueryWrapper<>(); int isDeletUserAuth = sysUserAuthMapper.delete(userAuthLambdaQueryWrapper); if (isDeletUserAuth == 0){ throw new QLException(USER_REMOVE_ERROR); }
-
清除权限缓存:从Redis中删除该用户的权限缓存。
String key = USER_ROLE_CACHE + userId; redisTemplate.delete(key);
-
返回结果:返回删除操作的结果。
return ResSerialization.success();
2.3 更新用户信息
updataUser
方法实现了更新用户信息的功能。具体流程如下:
-
查询用户信息:根据用户ID查询用户信息,如果用户不存在,抛出
USER_UPDATE_IS_EXIST_ERROR
异常。SysUser user = this.getById(userUpdateVO.getSysUserId()); if (ObjectUtil.isEmpty(user)){ throw new QLException(USER_UPDATE_IS_EXIST_ERROR); }
-
更新密码:如果请求中指定了重置密码或使用默认密码,则更新用户的密码。
Boolean isDefaultPass = userUpdateVO.getDefaultPass(); if (isDefaultPass){ userAuthService.updateUserPassword(userId,DEFAULT_PASSWORD); } Boolean resetPass = userUpdateVO.getResetPass(); if (resetPass != null && resetPass && !isDefaultPass){ String confirmPwd = userUpdateVO.getConfirmPwd(); String password = new String(Base64.getDecoder().decode(confirmPwd)); userAuthService.updateUserPassword(userId,password); }
-
更新用户信息:将
UserUpdateVO
中的属性复制到SysUser
对象中,并更新用户表。SysUser sysUser = BeanUtil.copyProperties(userUpdateVO, SysUser.class); sysUser.setIsAdmin(userUpdateVO.getIsAdmin()); LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>(); sysUserLambdaQueryWrapper.eq(SysUser::getSysUserId,sysUserId); OperationUtil.executeOperation(() -> this.update(sysUser, sysUserLambdaQueryWrapper),USER_MSG_UPDATE_ERROR);
-
返回结果:返回更新操作的结果。
return ResSerialization.success();
2.4 新增用户
insertUser
方法实现了新增用户的功能。具体流程如下:
-
校验手机号和用户名:检查手机号和用户名是否已存在,如果存在则抛出相应的异常。
LambdaQueryWrapper<SysUser> checkPhoneWrapper = new LambdaQueryWrapper<>(); checkPhoneWrapper.eq(SysUser::getTelphone,telphone); SysUser checkUserPhoneObj = this.getOne(checkPhoneWrapper); if (ObjectUtil.isNotEmpty(checkUserPhoneObj)){ throw new QLException(USER_PHONE_EXIST); } LambdaQueryWrapper<SysUser> checkUsernameWrapper = new LambdaQueryWrapper<>(); checkUsernameWrapper.eq(SysUser::getTelphone,userInsertVO.getTelphone()); SysUser checkUserNameObj = this.getOne(checkUsernameWrapper); if (ObjectUtil.isNotEmpty(checkUserNameObj)){ throw new QLException(USER_NAME_EXIST); }
-
保存用户信息:将
UserInsertVO
中的属性复制到SysUser
对象中,并设置创建时间和更新时间。SysUser sysUser = BeanUtil.copyProperties(userInsertVO, SysUser.class); Date date = new Date(); sysUser.setCreatedAt(date); sysUser.setUpdatedAt(date); sysUser.setIsAdmin(userInsertVO.getIsAdmin()); sysUser.setSysType(SYSTEM_TYPE_MGR); OperationUtil.executeOperation(() -> this.save(sysUser),USER_SAVE_ERROR);
-
保存用户认证信息:生成盐值,对默认密码进行加密,并保存到用户认证表中。
SysUserAuth sysUserAuth = new SysUserAuth(); sysUserAuth.setUserId(sysUser.getSysUserId()); String salt = IdUtil.fastSimpleUUID().substring(0, 6); sysUserAuth.setSalt(salt); String password = DEFAULT_PASSWORD + salt; String pw = PasswordUtil.hashPassword(password); sysUserAuth.setCredential(pw); sysUserAuth.setSysType(SYSTEM_TYPE_MGR); sysUserAuth.setIdentifier(userInsertVO.getLoginUsername()); sysUserAuth.setIdentityType(IDENTITY_TYPE_ACCOUNT); OperationUtil.executeOperation(() -> userAuthService.save(sysUserAuth) ,USER_SAVE_ERROR);
-
返回结果:返回新增操作的结果。
return ResSerialization.success();
2.5 修改用户状态
updataUserState
方法实现了修改用户状态的功能。具体流程如下:
-
更新用户状态:根据用户ID和请求中的状态值更新用户的状态。
Integer state = hashMap.get("state"); SysUser sysUser = new SysUser(); sysUser.setState(state); LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>(); sysUserLambdaQueryWrapper.eq(SysUser::getSysUserId,userId); OperationUtil.executeOperation(() -> this.update(sysUser, sysUserLambdaQueryWrapper),USER_STATUS_UPDATE_ERROR);
-
返回结果:返回更新操作的结果。
return ResSerialization.success();
2.6 查询用户详情
selectUserDetial
方法实现了查询用户详情的功能。具体流程如下:
-
参数校验:检查用户ID是否为空,如果为空则抛出
PARAMS_NOT_NULL
异常。if (userId == null){ throw new QLException(PARAMS_NOT_NULL); }
-
查询用户信息:根据用户ID查询用户信息。
SysUser sysUser = this.getById(userId);
-
返回结果:返回查询到的用户信息。
return ResSerialization.success(sysUser);
3. 权限控制
在UserController
中,使用了SaCheckRole
注解进行权限控制。例如,删除用户和修改用户状态的操作需要特定的角色权限才能执行。通过StpUtil.getLoginIdAsLong()
方法获取当前登录用户的ID,并进行权限校验。
@SaCheckRole(value = {"admin", "super_admin"}, mode = SaMode.OR)
@DeleteMapping("/{userId}")
ResSerialization<String> deleteUser(@PathVariable Long userId){
// 删除用户逻辑
}
@SaCheckRole(value = {"admin", "super_admin"}, mode = SaMode.OR)
@PutMapping("/status/{userId}")
ResSerialization<String> updataUserState(@PathVariable Long userId, @RequestBody HashMap<String , Integer> hashMap){
// 修改用户状态逻辑
}