【从0到1完成一个项目(六)】用户中心(下)

书接上回,上次完成了注册的后端部分,这次从登录开始。

登录的需求分析

登录接口

  • 接收参数:用户账户、密码(请求参数很长时,不建议用get请求,因为get请求会将参数拼接在url上,拼接的长度是有限制的)
  • 请求类型:POST
  • 请求体:JSON格式的数据
  • 返回值:用户信息(脱敏处理)

登录逻辑

  1. 校验用户账户和密码是否合法(这都不对的话,就省去到数据库查询,降低数据库的压力,节省资源)
    a. 非空
    b.账户长度不小于4位
    c.密码不小于八位
    d.账户不包含特殊字符
  2. 校验密码是否输入正确。要和数据库中的密文密码去对比
  3. 用户信息脱敏(隐藏敏感信息,防止数据库中的字段泄露)
  4. 用session记录用户的登录态,将其存到服务器上(用后端的Springboot框架自带的服务器tomcat去记录。session是servlet的api,而tomcat内核里用到的就是sevlet,所以是存在tomcat里)然后前端再次访问服务器的时候,就会带着一个由服务器分配的cookie,然后服务器就根据这个cookie找到对应的session,这就实现了记住前端是哪个用户。
  5. 返回给前端脱敏后的用户信息

网上找的解释(更加完善)
在这里插入图片描述

写代码流程

  • 先做需求分析
  • 然后设计接口
  • 写方法实现
  • 持续优化!!!!(在写的过程中,对相同代码、常量提取,进而达到复用,这种优化技巧就要平时多看多写)

前后端交互

  • 前端通过AJAX(js代码)来给后端发送请求
  • JQury是JS的封装库,可以直接调用里面的Ajax方法,只要在页面上引入库就行,这样就不要写原生的ajax
  • 此外,axios对ajax进行了封装,使得发送请求更加简单(完成相同的功能写的代码更少了)在页面上引入:
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  • 说回我们这个项目,前端采用的是开箱即用的ant design pro,里面是利用requset发送请求,可以理解request是更高一级的封装。

题外话:可以发现技术的不断迭代就是一个让人越来越懒的过程,代码的封装程度越来越高,框架那更是对基本API的疯狂封装(springmvc对servlet等的封装)。所以学技术可以试着探寻这个技术是如何来的,从什么原生技术封装而来,不要重复造轮子和不会造轮子是两码事。

  • 在写前端的时候,追溯request源码:用到了umi的插件、requestConfig是相关的配置(这个在写前端的时候再深入分析)

写登录的后端代码

从UserService接口开始写(因为Controller只负责接受参数,接受完参数给Service层处理业务逻辑)

/**
request 是为了获取session的
*/
User doLogin(String userAccount,String userPassword,HttpServletRequest request)

写登录方法的实现

将光标放在方法名上,ALT+ENTER,选择实现,直接跳到实现类

@Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //1.校验
        if(StringUtils.isAnyBlank(userAccount,userPassword)){
            return null;
        }
        if(userAccount.length()<4){
            return null;
        }
        if(userPassword.length() < 8){
            return null;
        }
        //2.账户不能包含特殊字符
        String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
        Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
        if(matcher.find()){
            return null;
        }
        //5.加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        //查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount",userAccount);
        queryWrapper.eq("userPassword",encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null){
            log.info("user login failed,userAccount cannot match userPassword");
            return null;
        }
        //用户脱敏
        User safetyUser = getSafetyUser(user);
        //记录用户的登录态
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE,safetyUser);
        return safetyUser;
    }
    @Override
    public User getSafetyUser(User originUser){
        User safetyuser = new User();
        safetyuser.setId(originUser.getId());
        safetyuser.setUsername(originUser.getUsername());
        safetyuser.setUserAccount(originUser.getUserAccount());
        safetyuser.setAvatarUrl(originUser.getAvatarUrl());
        safetyuser.setGender(originUser.getGender());
        safetyuser.setUserRole(originUser.getUserRole());
        safetyuser.setPhone(originUser.getPhone());
        safetyuser.setEmail(originUser.getEmail());
        safetyuser.setUserStatus(originUser.getUserStatus());
        safetyuser.setCreateTime(originUser.getCreateTime());
        return safetyuser;
    }

以上的代码实现就是相当于翻译之前写的登入逻辑,再一次体现了写代码的流程:先设计,再写代码。

优化代码

提取公共部分

比如注册和登录里面都用到加密的“盐【final String SALT = Kplusone】”,我们就直接提取到成员变量

/**
*盐值,加密密码
**/
private static final String SALT = "Kplusone";
加上日志

虽然我们没有自定义异常(后续再优化),但是可以记录一些日志【日志框架到时候再看楠老师的文档复习一下,现在就简单用一下,用项目需求带动知识点的学习。】

这里用一个Slf4j的注解,这里不需要引入sl4j的依赖,因为之前引入的lombok依赖包含了对sl4j的依赖。
将@Slf4j打在UserServiceImpl上,就可以在这个类中使用log,用log来记录日志,这样后面系统出问题,可以在日志中去查找,类似于监控。

用户脱敏

我们从数据库查到的用户信息包含了很多不能向用户展示的信息,比如:密码、用户更新时间、用户是否被删除(逻辑删除)
,所以专门写了一个用户脱敏的方法。

逻辑删除

在真实项目中,是不能将数据真正的从数据库中删除的,因为万一之后要查呢,所以我们都是使用逻辑删除,专门设计一个“isDelete”的字段,0表示没有删,1表示被删了。所以mybatis-plus底层封装删除方法其实是改这个字段的值,并不会真正执行delete语句,然后mybatis-plus查询的时候也不会查询被逻辑删除的数据。当然这个功能需要我们在application中配置。具体怎么配置可以查mybatis-plus的官方文档,直接搜逻辑删除就行。
在这里插入图片描述
在这里插入图片描述

登录接口

开始写Controller,要把业务接口封装成处理请求的Controller
Controller层倾向对请求参数本身的校验,不涉及业务逻辑本身(越少越好)
Service层是对业务逻辑的校验,(除了被Controller调用以外,还有可能被其他类调用,比如Service层互相调用)

在Controller包中新建UserController

专门用来处理和用户业务相关的请求

加上@RestController

这个注解有两个作用:

  • 表明这个是一个处理前端发送过来的请求的类
  • 类返回给前端的数据格式是application.json
加上@RequestMapping

定义这个类是接受那个路径的请求,这个写在类上,就是公共部分
@RequestMapping(“/user”)

介绍一个插件

Auto filling Java Call arguments,自动填充Java的方法参数。安装好重启就能用。

注册请求

在这里插入图片描述
将光标放在括号里,按下ALT+ENETR,选择“自动填充参数”,就可以补齐参数

不过这里我们最好将前端发送过来的json格式的参数在后端封装成一个对象
在这里插入图片描述
写上注释:用户注册请求参数的封装,然后这里可以实现Serilizable,就是序列化(应该是考虑了网络传输,关于序列化的知识单独出一篇博客)
实现序列化接口,右键选择"生成"->“serialVersionUID”,生成序列化ID,如果没有serialVersionUID,进行下面的设置:
在这里插入图片描述
设置完成后,光标放在UserRegisterRequest,ALT+enter,选择“添加序列号字段”。
在这里插入图片描述
在这里插入图片描述
加上@Data注解,自动生成get\set方法

Controller就使用封装好的请求参数

在这里插入图片描述
给参数加上@RequestBody,目的就是告诉SpringMvc将前端传来的JSON参数和UserRegisterRequest关联上。
最后,完善一下检验参数的代码
在这里插入图片描述
用同样的方式写登录接口:
在这里插入图片描述
在这里插入图片描述

测试

不用Postman,因为在idea自带一个测试工具
在这里插入图片描述
在这里插入图片描述
以Debug的模式启动项目
在这里插入图片描述
添加POST请求,把自动生成的范例删掉
在这里插入图片描述
在这里插入图片描述
可以在想测试的地方打断点。
在这里插入图片描述
点击发送请求的按钮
在这里插入图片描述
返回结果:
在这里插入图片描述
在这里插入图片描述
再测试一下逻辑删除,把user表中一条数据的字段改为1,再次运行测试工具,这个时候是没有返回值的,因为现在这条数据已经不存在了,所以登录失败,测试完毕

写用户管理接口(其实就是查询和删除用户)

需求

!!!必须鉴权(这两个接口只能管理员使用)

  • 查询用户(允许根据用户名查询)
  • 删除用户
如何鉴权

用到User表的一个字段,userRole(0-普通用户,1-管理员)
因为要执行查训和删除,那肯定是已经登录成功了,此时服务器的session里已经保存了用户信息,那么我们在执行查询和删除前先拿到用户的登录状态,从里面获得用户的UserRole的值,进行判断达到鉴权的目的。
在这里插入图片描述
这个时候,getAttribute应该取什么呢?之前我们在UserServiceImpl类里面定义了一个用户状态登录键,提取到UserService接口,因为接口里的成员变量默认是public static final.
在这里插入图片描述

参数username可以为空,如果是空,那就是查询所有,不为空,那就是查询一部分。
注意:这里前后端数据交互是采用json,所以并不用加从路径上获取参数的注解,并且json格式的参数也不会出现在路经上。
因为是GET请求,所以不用加@RequestBody注解,框架应该会自动转为json.POST请求就应该加上注解。

在这里插入图片描述
一边写代码,要一边优化,因为在经验不足的情况下,都是一边做一边优化,等有经验了,就可以有一个提前的预判,写的代码就可以一步到位。

对常量进行优化

新建一个常量包
在这里插入图片描述
新建一个UserConstant接口
在这里插入图片描述
优化完以后,把之前UserService里的用户登录状态键删掉,然后把引起的相关问题改一改,很简单的。
然后把用到1的地方改为ADMIN_ROLE

删除接口

在这里插入图片描述
继续优化,这两个接口鉴权的代码是一样的,可以提取出来,直接贴出最后的代码

//参数username可以为空,如果是空,那就是查询所有,不为空,那就是查询一部分。
    //注意:这里前后端数据交互是采用json,所以并不用加从路径上获取参数的注解,并且json格式的参数也不会出现在路经上。
    //因为是GET请求,所以不用加@RequestBody注解,框架应该会自动转为json.POST请求就应该加上注解。
    @GetMapping("/search")
    public List<User> searchUsers(String username,HttpServletRequest request){
        if (!isAdmin(request)){
            return new ArrayList<User>();
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if(StringUtils.isNotBlank(username)){
            queryWrapper.like("username",username);
        }
        List<User> userList = userService.list(queryWrapper);
        return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
    }
    @PostMapping("/delete")
    public boolean deleteUser(@RequestBody long id,HttpServletRequest request){
        if(!isAdmin(request)){
            return false;
        }
        //id一定不会为空,所以不用校验,拿到直接判断
        if(id <= 0){
            return false;
        }
        return userService.removeById(id);//这是逻辑删除
    }

    /**
     * 判断是否是管理员
     * @param request
     * @return
     */
    private boolean isAdmin(HttpServletRequest request){
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        User user = (User) userObj;
        if(user == null || user.getUserRole() != UserConstant.ADMIN_ROLE){
            return false;
        }
        return true;
    }
添加session的有效期(1天)
Spring:
 session:86400
测试鉴权

先去除之前的登录态(刚才测试的)
在这里插入图片描述
再以debug的模式启动,找到之前的测试历史
在这里插入图片描述
把一条数据的isDelete改为0,userRole改为1,点击上传按钮
在这里插入图片描述
再次运行登录(目的是让服务器记住这个用户信息,下面好测试删除和查询)
在这里插入图片描述
测试查询,点击左侧的绿色小图标
在这里插入图片描述
运行,得到返回值
在这里插入图片描述
在这里插入图片描述
这里其实还是有问题,没有做用户脱敏
那就来脱敏,反正之前UserService里写登录的时候就已经写过getSafeUser()方法,这里直接调用就行
在这里插入图片描述
这里用了函数式编程(这块编程技巧之后专门出一个博客复习一下)
再优化一下,光标放到return,出现黄色灯泡。点击,选择第一个,直接简写,搞定!
在这里插入图片描述
在这里插入图片描述

在写这个博客时候,埋了很多坑(以后要复习的知识),之后都要给它填上。
到这,后端代码可以停一下了,下一篇写前端了。
参考资料:别人的笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kplusone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值