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

简介: 如果前端与后台接口之间不存在跨域问题,那么推荐使用cookie和session来记录登录状态。如果前端与服务器接口之间存在跨域问题,那么就要使用token的方式来维持登录状态。

登录业务流程


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


② 调用后台接口进行验证


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


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


  • http 是无状态的


  • 通过 cookie 在客户端记录状态


  • 通过 session 在服务器端记录状态


  • 通过 token 方式维持状态


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

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

1 需求与逻辑分析


1.1 页面展示


1). 页面原型展示


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


26882603759742a28c77d99ae3bae591.png


2). 查看登录请求


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


243dd253ad2c449e8be91c725682c259.png


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


0a81eba4579e4da9bfb95b702ca1c0bf.png


3). 数据模型(employee表)


319f2cae6c0c48d79bfc24a50a9de290.png


4). 前端页面分析


df7443b2fb864304ae8c5597e3dd85ec.png


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


d5c28748b2cb4507a6db2b05c5e93dc8.png


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


cd5cd1b8cc6e4aaa87bdfec036b1a071.png


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


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


1.2 登陆逻辑分析


1.2.1 登陆逻辑


4a16b203dc074dd6b4d9bfc10157e8e5.png


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


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


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


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


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


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


1.3 登出逻辑


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


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


1). 退出页面展示


e0037f7fb0e746cf8fde18428b82dcf6.png


2).前端页面分析


0ea3c0f9af73458aa30441da46245739.png


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


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


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


35d46fb22f8a4cafb5777666638ec9f8.png


2  代码开发


2.1 基础准备工作


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


9623826c739e4feab67d0a4904f935cf.png


1). 创建实体类Employee


所属包: com.itheima.reggie.entity


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


2). 定义Mapper接口


所属包: com.itheima.reggie.mapper


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


3).Service接口


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


4). Service实现类


package com.itheima.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.mapper.EmployeeMapper;
import com.itheima.reggie.service.EmployeeService;
import org.springframework.stereotype.Service;
/**
 * Description: new java files header..
 * @author w
 * @version 1.0
 * @date 2022/8/12 11:15
 */
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements EmployeeService{
}


5). Controller基础代码


所属包: com.itheima.reggie.controller


package com.itheima.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
 * Description: new java files header..
 * @author w
 * @version 1.0
 * @date 2022/8/12 11:17
 */
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
    /**@Description: 员工登陆
     * @param @RequestBody 传入的是json 故需要将其转化为实体类,json中的类名与实体类名对应才可以封装
     *          A. 由于需求分析时, 我们看到前端发起的请求为post请求, 所以服务端需要使用注解 @PostMapping
     *          B. 由于前端传递的请求参数为json格式的数据, 这里使用Employee对象接收, 但是将json格式数据封装到实体类中, 在形参前需要加注解@RequestBody
     * @return com.itheima.reggie.common.R<com.itheima.reggie.entity.Employee>
     * @version v1.0
     * @author LiBiGo
     * @date 2022/8/12 11:32
     */
        //  ①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        //  ②. 根据页面提交的用户名username查询数据库中员工数据信息
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
        Employee emp = employeeService.getOne(queryWrapper); //在数据库中用户名是唯一的,所以可以用getone()
        //  ③. 如果没有查询到, 则返回登录失败结果
        if (emp == null){
            return R.error("用户名不存在");
        }
        //  ④. 密码比对,如果不一致, 则返回登录失败结果
        if(!emp.getPassword().equals(password)){
            return R.error("密码失败");
        }
        //  ⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if(emp.getStatus() == 0){
            return R.error("账户已禁用");
        }
        //  ⑥. 登录成功,将员工id存入Session, 并返回登录成功结果
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
    /**@Description: 需要在Controller中创建对应的处理方法, 接收页面发送的POST请求 /employee/logout
     * @version v1.0
     * @author LiBiGo
     * @date 2022/8/12 12:09
     */
        //  清理Session中保存的当前登录员工的id
        request.getSession().removeAttribute("employee");
        //  返回结果
        return R.success("退出成功");
    }
}


6). 导入通用结果类R


package com.itheima.reggie.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
 * 通用返回结果,服务端响应的数据最终都会封装成此对象
 * @param <T>
 * A. 如果业务执行结果为成功, 构建R对象时, 只需要调用 success方法;
 *      如果需要返回数据传递 object 参数, 如果无需返回, 可以直接传递null。
 * B. 如果业务执行结果为失败, 构建R对象时, 只需要调用 error方法, 传递错误提示信息即可。
 */
@Data
public class R<T> {
    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据
    private Map map = new HashMap(); //动态数据
    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }
    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }
    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
}


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。


退出前:


02ab8654c2484c80a50545709e36a02e.png


 退出后:


35324179874848d69b07fccbb0b167e7.png


目录
相关文章
|
10天前
|
安全 Java API
SpringBoot + 事务钩子函数,打造高效支付系统!
【8月更文挑战第9天】在当今快速发展的数字支付时代,构建一个稳定、高效且安全的支付系统是企业数字化转型的关键一步。SpringBoot以其简洁的配置、快速的开发速度以及强大的生态支持,成为了构建支付系统的热门选择。而结合事务钩子函数(Transaction Hooks),则能进一步确保支付流程的完整性、一致性和可维护性。以下,我将分享如何利用SpringBoot与事务钩子函数来打造高效支付系统的技术实践。
39 15
SpringBoot + 事务钩子函数,打造高效支付系统!
|
5天前
|
Java Maven
构建Springboot项目、实现简单的输出功能、将项目打包成可以执行的JAR包(详细图解过程)
这篇文章详细介绍了构建SpringBoot项目的过程,包括新建工程、选择环境配置、添加依赖、项目结构说明,并演示了如何编写一个简单的Controller控制器实现输出功能,最后讲解了如何使用Maven将项目打包成可执行的JAR包,并提供了运行JAR包的命令和测试效果。
构建Springboot项目、实现简单的输出功能、将项目打包成可以执行的JAR包(详细图解过程)
|
5天前
|
Java 数据库连接 mybatis
基于SpringBoot+MyBatis的餐饮点餐系统
本文介绍了一个基于SpringBoot和MyBatis开发的餐饮点餐系统,包括系统的主控制器`IndexController`的代码实现,该控制器负责处理首页、点餐、登录、注册、订单管理等功能,适用于毕业设计项目。
9 0
基于SpringBoot+MyBatis的餐饮点餐系统
|
6天前
|
SQL 前端开发 Java
springboot项目中使用shiro实现用户登录以及权限的验证
这篇文章详细介绍了如何在Spring Boot项目中集成Apache Shiro框架来实现用户登录和权限验证,包括项目依赖配置、数据库连接、实体类定义、控制器、服务层、Mapper层以及前端页面的实现,并展示了实际效果和过滤器代码。
springboot项目中使用shiro实现用户登录以及权限的验证
|
4天前
|
存储 NoSQL Java
基于SpringBoot+Redis实现查找附近用户的功能
使用Redis的GEO命令结合SpringBoot实现查找附近用户的功能,通过`GEOADD`命令添加地理位置信息和`GEORADIUS`命令查询附近用户。
9 0
|
4天前
|
存储 NoSQL Redis
基于SpringBoot+Redis实现点赞/排行榜功能,可同理实现收藏/关注功能,可拓展实现共同好友/共同关注/关注推送功能
在SpringBoot项目中使用Redis的Set和ZSet集合实现点赞和排行榜功能,并通过示例代码展示了如何使用`stringRedisTemplate`操作Redis来完成这些功能。
26 0
|
4天前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
18 0
|
5天前
|
前端开发 JavaScript Java
解决springboot+vue+mybatis中,将后台数据分页显示在前台,并且根据页码自动跳转对应页码信息
该博客文章讲述了如何在Spring Boot + Vue + MyBatis的项目中实现后台数据的分页查询,并在前端进行显示和页码跳转,包括后端的分页查询实现、前端与后端的交互以及使用Element UI进行分页展示的方法。
|
6天前
|
JavaScript 前端开发 网络协议
WebSocket在Java Spring Boot+Vue框架中实现消息推送功能
在现代Web应用中,实时消息提醒是一项非常重要的功能,能够极大地提升用户体验。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为实现实时消息提醒提供了高效且低延迟的解决方案。本文将详细介绍如何在Java Spring Boot后端和Vue前端框架中利用WebSocket实现消息提醒功能。
20 0
|
6天前
|
NoSQL 安全 Java
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权