• 基于Springboot外卖系统04:后台系统用户登录+登出功能


    登录业务流程

    ① 在登录页面输入用户名和密码

    ② 调用后台接口进行验证

    ③ 通过验证之后,根据后台的响应状态跳转到项目主页

    2. 登录业务的相关技术点

    • http 是无状态的
    • 通过 cookie 在客户端记录状态
    • 通过 session 在服务器端记录状态
    • 通过 token 方式维持状态

    如果前端与后台接口之间不存在跨域问题,那么推荐使用cookie和session来记录登录状态。

    如果前端与服务器接口之间存在跨域问题,那么就要使用token的方式来维持登录状态。

    1 需求与逻辑分析

    1.1 页面展示

    1). 页面原型展示

    登录页面存放目录 /resources/backend/page/login/login.html

    2). 查看登录请求

    通过浏览器调试工具(F12),可以发现,点击登录按钮时,页面会发送请求(请求地址为http://localhost:8080/employee/login)并提交参数 username和password, 请求参数为json格式数据

     

    404,因为后台系统还没有响应此请求的处理器,所以需要创建相关类来处理登录请求 ;

     3). 数据模型(employee表)

     4). 前端页面分析

     当点击 "登录" 按钮, 会触发Vue中定义的 handleLogin 方法:

     在上述的前端代码中, 大家可以看到, 发送登录的异步请求之后, 获取到响应结果, 在响应结果中至少包含三个属性: code、data、msg 。

     由前端代码,我们也可以看到,在用户登录成功之后,服务端会返回用户信息,而前端是将这些用户信息,存储在客户端的 localStorage 中了。

    localStorage.setItem('userInfo',JSON.stringify(res.data))

    1.2 登陆逻辑分析

    1.2.1 登陆逻辑

    ①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串

    ②. 根据页面提交的用户名username查询数据库中员工数据信息 

    ③. 如果没有查询到, 则返回登录失败结果

    ④. 密码比对,如果不一致, 则返回登录失败结果

    ⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果

    ⑥. 登录成功,将员工id存入Session, 并返回登录成功结果

    1.3 登出逻辑

    在后台管理系统中,管理员或者员工,登录进入系统之后,页面跳转到后台系统首页面(backend/index.html),此时会在系统的右上角显示当前登录用户的姓名。

    如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转回登录页面。

    1). 退出页面展示

     2).前端页面分析

    点击将会调用一个js方法logout, 在logout的方法中执行如下逻辑:

    A. 发起post请求, 调用服务端接口 /employee/logout 执行退出操作 ;

    B. 删除客户端 localStorage 中存储的用户登录信息, 跳转至登录页面 ;

    2  代码开发

    2.1 基础准备工作

    在进行登录功能的代码实现之前, 首先在我们的工程下创建包结构:

     1). 创建实体类Employee

    所属包: com.itheima.reggie.entity

    1. package com.itheima.reggie.entity;
    2. import com.baomidou.mybatisplus.annotation.FieldFill;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import lombok.Data;
    5. import java.io.Serializable;
    6. import java.time.LocalDateTime;
    7. /**@Description: 员工实体类 该实体类主要用于和员工表 employee 进行映射。
    8. * @version v1.0
    9. * @author LiBiGo
    10. * @date 2022/8/12 11:05
    11. */
    12. // 在实体类上添加@Data注解,可以省去代码中大量的 get()、 set()、 toString() 等方法,提高代码的简洁:
    13. @Data
    14. public class Employee implements Serializable {
    15. private static final long serialVersionUID = 1L;
    16. private Long id;
    17. private String username;
    18. private String name;
    19. private String password;
    20. private String phone;
    21. private String sex;
    22. // map-underscore-to-camel-case: true
    23. // 在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    24. private String idNumber; //身份证 因为在配置文件中设置驼峰命名,所以与数据库中的不太一样,数据库中为id_number
    25. private Integer status;
    26. private LocalDateTime createTime; // 同上 驼峰命名法
    27. private LocalDateTime updateTime; // 同上 驼峰命名法
    28. @TableField(fill = FieldFill.INSERT)
    29. private Long createUser;
    30. @TableField(fill = FieldFill.INSERT_UPDATE)
    31. private Long updateUser;
    32. }

    2). 定义Mapper接口

    所属包: com.itheima.reggie.mapper

    1. package com.itheima.reggie.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.itheima.reggie.entity.Employee;
    4. import org.apache.ibatis.annotations.Mapper;
    5. /**
    6. * Description: 在MybatisPlus中, 自定义的Mapper接口, 需要继承自 BaseMapper。
    7. * @author w
    8. * @version 1.0
    9. * @date 2022/8/12 11:11
    10. */
    11. @Mapper
    12. public interface EmployeeMapper extends BaseMapper {
    13. }

    3).Service接口

    1. package com.itheima.reggie.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.itheima.reggie.entity.Employee;
    4. /**@Description: 本Service接口, 在定义时需要继承自MybatisPlus提供的Service层接口 IService,
    5. * 这样就可以直接调用 父接口的方法直接执行业务操作, 简化业务层代码实现。
    6. * @version v1.0
    7. * @author LiBiGo
    8. * @date 2022/8/12 12:20
    9. */
    10. public interface EmployeeService extends IService {
    11. }

    4). Service实现类

    1. package com.itheima.reggie.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.itheima.reggie.entity.Employee;
    4. import com.itheima.reggie.mapper.EmployeeMapper;
    5. import com.itheima.reggie.service.EmployeeService;
    6. import org.springframework.stereotype.Service;
    7. /**
    8. * Description: new java files header..
    9. * @author w
    10. * @version 1.0
    11. * @date 2022/8/12 11:15
    12. */
    13. @Service
    14. public class EmployeeServiceImpl extends ServiceImpl implements EmployeeService{
    15. }

    5). Controller基础代码

    所属包: com.itheima.reggie.controller

    1. package com.itheima.reggie.controller;
    2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    3. import com.itheima.reggie.common.R;
    4. import com.itheima.reggie.entity.Employee;
    5. import com.itheima.reggie.service.EmployeeService;
    6. import lombok.extern.slf4j.Slf4j;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.util.DigestUtils;
    9. import org.springframework.web.bind.annotation.PostMapping;
    10. import org.springframework.web.bind.annotation.RequestBody;
    11. import org.springframework.web.bind.annotation.RequestMapping;
    12. import org.springframework.web.bind.annotation.RestController;
    13. import javax.servlet.http.HttpServletRequest;
    14. /**
    15. * Description: new java files header..
    16. * @author w
    17. * @version 1.0
    18. * @date 2022/8/12 11:17
    19. */
    20. @Slf4j
    21. @RestController
    22. @RequestMapping("/employee")
    23. public class EmployeeController {
    24. @Autowired
    25. private EmployeeService employeeService;
    26. @PostMapping("/login")
    27. public R login(HttpServletRequest request, @RequestBody Employee employee){
    28. /**@Description: 员工登陆
    29. * @param @RequestBody 传入的是json 故需要将其转化为实体类,json中的类名与实体类名对应才可以封装
    30. * A. 由于需求分析时, 我们看到前端发起的请求为post请求, 所以服务端需要使用注解 @PostMapping
    31. * B. 由于前端传递的请求参数为json格式的数据, 这里使用Employee对象接收, 但是将json格式数据封装到实体类中, 在形参前需要加注解@RequestBody
    32. * @return com.itheima.reggie.common.R
    33. * @version v1.0
    34. * @author LiBiGo
    35. * @date 2022/8/12 11:32
    36. */
    37. // ①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串
    38. String password = employee.getPassword();
    39. password = DigestUtils.md5DigestAsHex(password.getBytes());
    40. // ②. 根据页面提交的用户名username查询数据库中员工数据信息
    41. LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    42. queryWrapper.eq(Employee::getUsername,employee.getUsername());
    43. Employee emp = employeeService.getOne(queryWrapper); //在数据库中用户名是唯一的,所以可以用getone()
    44. // ③. 如果没有查询到, 则返回登录失败结果
    45. if (emp == null){
    46. return R.error("用户名不存在");
    47. }
    48. // ④. 密码比对,如果不一致, 则返回登录失败结果
    49. if(!emp.getPassword().equals(password)){
    50. return R.error("密码失败");
    51. }
    52. // ⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
    53. if(emp.getStatus() == 0){
    54. return R.error("账户已禁用");
    55. }
    56. // ⑥. 登录成功,将员工id存入Session, 并返回登录成功结果
    57. request.getSession().setAttribute("employee",emp.getId());
    58. return R.success(emp);
    59. }
    60. @PostMapping("/logout")
    61. public R logout(HttpServletRequest request){
    62. /**@Description: 需要在Controller中创建对应的处理方法, 接收页面发送的POST请求 /employee/logout
    63. * @version v1.0
    64. * @author LiBiGo
    65. * @date 2022/8/12 12:09
    66. */
    67. // 清理Session中保存的当前登录员工的id
    68. request.getSession().removeAttribute("employee");
    69. // 返回结果
    70. return R.success("退出成功");
    71. }
    72. }

    6). 导入通用结果类R

    1. package com.itheima.reggie.common;
    2. import lombok.Data;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. /**
    6. * 通用返回结果,服务端响应的数据最终都会封装成此对象
    7. * @param
    8. * A. 如果业务执行结果为成功, 构建R对象时, 只需要调用 success方法;
    9. * 如果需要返回数据传递 object 参数, 如果无需返回, 可以直接传递null。
    10. * B. 如果业务执行结果为失败, 构建R对象时, 只需要调用 error方法, 传递错误提示信息即可。
    11. */
    12. @Data
    13. public class R {
    14. private Integer code; //编码:1成功,0和其它数字为失败
    15. private String msg; //错误信息
    16. private T data; //数据
    17. private Map map = new HashMap(); //动态数据
    18. public static R success(T object) {
    19. R r = new R();
    20. r.data = object;
    21. r.code = 1;
    22. return r;
    23. }
    24. public static R error(String msg) {
    25. R r = new R();
    26. r.msg = msg;
    27. r.code = 0;
    28. return r;
    29. }
    30. public R add(String key, Object value) {
    31. this.map.put(key, value);
    32. return this;
    33. }
    34. }

    3 功能测试

    3.1 登陆功能测试

    代码实现完毕后, 启动项目, 访问url: http://localhost:8080/backend/page/login/login.html , 进行登录测试。

    在测试过程中, 可以通过debug断点调试的方式来跟踪程序的执行过程,并且可以查看程序运行时各个对象的具体赋值情况。而且需要注意, 在测试过程中,需要将所有的情况都覆盖到。

    3.2 登出功能测试

    5.3 功能测试

    1). 代码实现完毕后, 重启服务, 访问登录界面 http://localhost:8080/backend/page/login/login.html ;

    2). 登录完成之后, 进入到系统首页 backend/index.html, 点击右上角退出按钮执行退出操作, 完成后看看是否可以跳转到登录页面 , 并检查localStorage。

    退出前:

      退出后:

     

  • 相关阅读:
    涉及法律诉讼和负债670万美元的【工务园】申请纳斯达克IPO上市
    一个由public关键字引发的bug
    VTK 基础入门 ( 一 ) 基本实例超详解释
    软件面试笔试复习之C语言
    【单片机基础】ADC0832详解
    高校教务系统登录页面JS分析——合肥工业大学
    用云手机运营TikTok有什么好处?
    Redis数据结构:字符串
    手摸手带你 在Windows系统中安装Istio
    【设计模式】Java 设计模式之代理模式(Proxy Pattern)
  • 原文地址:https://blog.csdn.net/qq_39237205/article/details/126301580