一、前言
在上一章中我对个人博客项目完成了登录接口,通过登录接口来整合redis和jwt,同时为创建UtilController,将一些公共接口放置在UtilController中
本章主要是完善上一章没有做完的功能, 主要是用户模块和完善登录接口, 包括用户的增删改查细节处理, 和登录接口的待完善内容(用户登录次数, 最后登录时间, 登录ip)
二, 用户模块增删改查
新增用户的注册时间,登录次数填充, 注册地ip, 信息判断, 密码加密
密码加密新增pom依赖和PasswordUtils工具类
<!-- 实现SHA265加密--> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version> 1.15</version> </dependency> 复制代码
/** * 密码工具类 * @Author ningxuan * @Date 2022/8/8 20:30 */ public class PasswordUtils { /*** * 利用Apache的工具类实现SHA-256加密 * * @param str 加密后的报文 * @return */ public static String getSHA256Str(String str) { MessageDigest messageDigest; String encdeStr = ""; try { messageDigest = MessageDigest.getInstance("SHA-256"); byte[] hash = messageDigest.digest(str.getBytes("UTF-8")); encdeStr = Hex.encodeHexString(hash); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return encdeStr; } } 复制代码
SysUserController
@PostMapping @ApiOperation("新增") public ResultVo insert(@RequestBody SysUserInsertDto userDto, HttpServletRequest request){ // 判断username是否重复 true:null boolean b = sysUserService.userIsOnly(userDto.getUsername()); if (!b){ throw new BlogException(ErrorEnum.USER_EXIST_ERROR); } // 新增用户 sysUserService.insert(userDto,request); return new ResultVo(); } 复制代码
SysUserServiceImpl
// 新增 @Override public void insert(SysUserInsertDto userDto, HttpServletRequest request) { SysUser user = new SysUser(); BeanUtils.copyProperties(userDto, user); user.setNum(1); user.setStatus("1"); user.setCreateTime(LocalDateTime.now()); // 获取用户注册ip String ipAddr = IpUtils.getIpAddr(request); user.setIpconfig(ipAddr); // 如果用户未填昵称则默认为username if (StringUtils.isEmpty(userDto.getNickname())){ user.setNickname(userDto.getUsername()); } // 密码加密 user.setPassword(PasswordUtils.getSHA256Str(user.getPassword())); this.save(user); } // 判断username是否唯一 @Override public boolean userIsOnly(String username) { LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getUsername, username); SysUser user = this.getOne(wrapper); return user == null; } 复制代码
新建IPUtil类,用到了Common-lang3 jar包
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8</version> </dependency> 复制代码
用到了里面的StringUtils.isEmpty(final CharSequence cs)方法, 用来判断字符串是否为null
IpUtils工具类来自于人人开源,具体如下
public class IpUtils { private static Logger logger = LoggerFactory.getLogger(IpUtils.class); /** * 获取IP地址 * * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 */ public static String getIpAddr(HttpServletRequest request) { String unknown = "unknown"; String ip = null; try { ip = request.getHeader("x-forwarded-for"); if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } catch (Exception e) { logger.error("IPUtils ERROR ", e); } return ip; } } 复制代码
逻辑删除
使用了mybatis-plus的逻辑删除
网络异常,图片无法展示
|
修改时间填充, username不允许修改等
@Override public void updateUser(SysUserUpdateDto userDto) { SysUser byId = this.getById(userDto.getBlid()); if (byId == null){ throw new BlogException(ErrorEnum.NOT_USER_ERROR); } SysUser user= new SysUser(); BeanUtils.copyProperties(userDto, user); // 更新修改时间 user.setUpdateTime(LocalDateTime.now()); this.updateById(user); } 复制代码
查看列表
@Override public Page<SysUser> getList(PageVo page) { LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); // 可以根据keyword进行用户名的模糊查询 wrapper.like(page.getKeyword() != null, SysUser::getNickname, page.getKeyword()) // 只查询用户id, 昵称, 头像地址, 用户名, 最后登录时间 .select(SysUser::getBlid,SysUser::getNickname,SysUser::getAvatar, SysUser::getUsername, SysUser::getLastLoginTime) // 查询账户状态正常的用户 .eq(SysUser::getStatus, "1"); Page<SysUser> pageList = new Page<>(page.getPage(), page.getSize()); Page<SysUser> list = this.page(pageList, wrapper); return list; } 复制代码
查看详情
@GetMapping @ApiOperation("查看详情") public ResultVo getInfo(Long blid){ LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getBlid, blid); SysUser one = sysUserService.getOne(wrapper); one.setPassword(null); return new ResultVo(one); } 复制代码
三、完善登录接口
用户不存在和密码错误使用自定义异常
@Override public SysUser login(LoginDto loginDto, HttpServletResponse response) { LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getUsername, loginDto.getUsername()); SysUser one = this.getOne(wrapper); if (one == null){ // 自定义异常 log.error("用户不存在"); throw new BlogException(ErrorEnum.USER_OR_PASSWORD_ERROR); } boolean equals = one.getPassword().equals(PasswordUtils.getSHA256Str(loginDto.getPassword())); if (!equals){ // 自定义异常 //TODO 密码错误之后生成token判断错误次数,超过五次锁用户 log.error("账号或密码错误"); throw new BlogException(ErrorEnum.USER_OR_PASSWORD_ERROR); } // 使用jwt生成token String token = JwtUtils.generateToken(one.getBlid()); // 使用redis保存token 默认8小时 redisUtils.set(RedisKeys.getLoginKey(token), one, CommonConstant.REDIS_TIME_TOKEN); // token存入cookie CookieUtils.setUpCookie(response, CommonConstant.COOKIE_TOKEN_KEY, token); return one; } 复制代码
完善之前留下的CookieUtil工具类中不能获取到application.yml文件的配置
pom依赖下载
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> 复制代码
类上添加@Component注解,主要是上面的依赖,不下载的话我添加注解也不好使
网络异常,图片无法展示
|
四、泛谈密码加密
明文存储密码毕竟有点不好, 本项目中采用了SHA265的形式存储密码
几种加密
由易到难可以分为
- 前端加密
- 前端、后端两至多次加密
- 前端、后端(密码+加密盐)加密
- 前端、后端(密码+加密盐+用户名)加密
- 加密盐随登录随机生成
五、测试
修改
这里我只按照id查询, 但是后面自动添加了status=“0”是因为我们之前设置逻辑删除了,具体可以看图
网络异常,图片无法展示
|
mybatis-plus逻辑删除注解 @TableLogic
网络异常,图片无法展示
|
删除
同样的因为我们设置了mybatis-plus 的逻辑删除,那么删除的时候是默认把我们设置的逻辑删除字段改为“1”
网络异常,图片无法展示
|
六、总结
详情具体的查SQL直接写到了controller层了, 感觉不太对, 但是目前对于Controller, Service我感觉对其还是了解的不够彻底,如果有大佬希望可以在评论区留下一些自己的见解, 谢谢