后台系统登录功能

1.需求分析

1.1页面原型展示

1.2查看登录请求

通过浏览器调试工具( F12 ),可以发现,点击登录按钮时,页面会发送请求( 并提交参数 username password, 请求参数为 json 格式数{"username":"admin","password":"123456"}
此时报 404 ,是因为我们的后台系统还没有响应此请求的处理器,所以我们需要创建相关类来处理登录请求 ;

1.3分析数据模型

1.4前端页面分析
在上述的前端代码中 , 大家可以看到 , 发送登录的异步请求之后 , 获取到响应结果 , 在响应结果中至少包含三个属性: code data msg
由前端代码,我们也可以看到,在用户登录成功之后,服务端会返回用户信息,而前端是将这些用户信息,存储在客户端的 localStorage 中了。
2. 代码开发
2.1 在工程下创建以下包结构:

 2.2 根据数据库的数据表,在entity包下,创建对应的实体类

package com.ning.reggie.entrty;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    private Long id;
    /**
     * 姓名
     */
    private String username;
    /**
     * 用户名,登录名
     */
    private String name;
    /**
     * 密码
     */
    private String password;
    /**
     * 手机号
     */
    private String phone;
    /**
     * 性别
     */
    private String sex;
    /**
     * 身份证号码
     */
    private String idNumber;
    /**
     * 状态
     * 1:正常
     * 0:禁用
     */
    private Integer status;
    /**
     * 创建时间
     * //@TableField:该注解用于标识非主键的字段。将数据库列与 JavaBean 中的属性进行映射
     * 在这里主要是通过fill属性,来指定字段的填充策略为	插入填充字段
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * 更新时间
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    /**
     * 创建人
     */
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    /**
     * 修改人
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

创建时间、修改时间、创建人、修改人

这几个字段为公共字段,在建表时,建议加入这几个字段,主要是用于做数据追溯

 2.3 在mapper包下,创建Mapper持久层接口

建议在定义接口时,在类名前加上大写i,增强代码的规范性和可阅读性

MybatisPlus , 自定义的 Mapper 接口 , 需要继承自 BaseMapper,并把实体类传入
所属包 : com.itheima.reggie.mapper

 注:记得在类上加@Mapper注解

package com.ning.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ning.reggie.entrty.Employee;
import org.apache.ibatis.annotations.Mapper;

/**
 * 员工mapper
 *
 * @author ning
 * @since 2022/10/31 20:03
 */
@Mapper
public interface IEmployeeMapper extends BaseMapper<Employee> {
}

2.4 在service包下,创建service业务层接口

本项目的 Service 接口 , 在定义时需要继承自 MybatisPlus 提供的 Service 层接口 IService,并把实体类传入, 这样就可以直接调用 父接口的方法直接执行业务操作, 简化业务层代码实现。
所属包 : com.itheima.reggie.service
package com.ning.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ning.reggie.entrty.Employee;

/**
 * 员工service接口
 *
 * @author ning
 * @since 2022/10/31 20:04
 */

public interface IEmployeeService extends IService<Employee> {
}

2.5 在service包下,先创建一个impl的包,然后在impl包下创建service业务层接口的实现类

 实现ServiceImpl,并把实体类的Mapper和实体类传入,并继承对应的service接口

 注:记得在类上加@Service注解

package com.ning.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ning.reggie.entrty.Employee;
import com.ning.reggie.mapper.IEmployeeMapper;
import com.ning.reggie.service.IEmployeeService;
import org.springframework.stereotype.Service;

/**
 * 员工业务实现
 *
 * @author ning
 * @since 2022/10/31 20:09
 */
@Service
public class EmployeeServiceImpl extends ServiceImpl<IEmployeeMapper, Employee> implements IEmployeeService{
}

2.6 在controller包下,创建controller表示层类

注:记得在类上加上@RestController、@RequestMapping注解

package com.ning.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ning.reggie.common.GlobalConstant;
import com.ning.reggie.common.R;
import com.ning.reggie.entrty.Employee;
import com.ning.reggie.service.IEmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 员工控制器
 *
 * @author ning
 * @since 2022/10/31 20:01
 */

/**
 * 日志
 */
@Slf4j

/**
 * //@RestController = @RequestBody + @Controller
 * //@RequestBody:注解就可以将对象进行反序列化
 * //@Controller:用来表示Spring某个类是否可以接受HTTP请求,她通常与@ResponseBody绑定使用。
 */
@RestController

/**
 * 用来处理请求地址映射的注解,可用于类或方法上。
 * 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
 */
@RequestMapping("/employee")
public class EmployeeController {

    //获取employeeService
    @Autowired
    private IEmployeeService employeeService;

   

2.7 导入通用结果类

http://t.csdn.cn/o4OY9

2.8 登录逻辑分析

建议:在设计数据表保存比较敏感的信息,例如密码,应该使用加密技术,加密之后存入,当需要调取数据,比对时接收到的数据按照同样的加密技术之后,再比较

处理逻辑如下:
. 将页面提交的密码 password 进行 md5 加密处理 , 得到加密后的字符串
. 根据页面提交的用户名 username 查询数据库中员工数据信息
. 如果没有查询到 , 则返回登录失败结果
. 密码比对,如果不一致 , 则返回登录失败结果
. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
. 登录成功,将员工 id 存入 Session, 并返回登录成功结果

2.9 代码实现

技术点说明 :
A. 由于需求分析时 , 我们看到前端发起的请求为 post 请求 , 所以服务端需要使用注解
@PostMapping
B. 由于前端传递的请求参数为 json 格式的数据 , 这里使用创建的实体类 对象接收 , 但是将 json 格式数据封装到实体类中, 在形参前需要加注解 @RequestBody
package com.ning.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ning.reggie.common.GlobalConstant;
import com.ning.reggie.common.R;
import com.ning.reggie.entrty.Employee;
import com.ning.reggie.service.IEmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 员工控制器
 *
 * @author ning
 * @since 2022/10/31 20:01
 */

/**
 * 日志
 */
@Slf4j

/**
 * //@RestController = @RequestBody + @Controller
 * //@RequestBody:注解就可以将对象进行反序列化
 * //@Controller:用来表示Spring某个类是否可以接受HTTP请求,她通常与@ResponseBody绑定使用。
 */
@RestController

/**
 * 用来处理请求地址映射的注解,可用于类或方法上。
 * 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
 */
@RequestMapping("/employee")
public class EmployeeController {

    //获取employeeService
    @Autowired
    private IEmployeeService employeeService;

    /**
     * 登录
     */
    //判断前端的请求路径
    //判断前端的请求方式
    //判断前端的请求参数
    //判断返回值
    @PostMapping("/login")
    public R<Employee> login(@RequestBody Employee employeeParam, HttpServletRequest request) {
        //前端传入的所有参数都是不可靠的,
        //用户名和密码的参数校验
        //用户名和密码,不能为空
        //参数校验
        if (StringUtils.isBlank(employeeParam.getUsername()) ||
                StringUtils.isBlank(employeeParam.getPassword())) {
            return R.error(GlobalConstant.FAILURE);
        }


        //判断一下是否可以接收到前端的参数
        log.info("前后端连接 ===> {}", employeeParam.toString());

        //将页面提交的密码password进行md5加密处理, 得到加密后的字符串
        //因为存入数据库中的密码就是经过md5加密过的,为了保证数据的安全性
        String md5p = DigestUtils.md5DigestAsHex(employeeParam.getPassword().getBytes());

        //根据页面提交的用户名username查询数据库中员工数据信息
       //创建LambdaQueryWrapper条件构造器对象,用来拼接查询语句
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        //拼接查询语句
        queryWrapper.eq(Employee::getUsername, employeeParam.getUsername());
        //调用service查询
        Employee employee = this.employeeService.getOne(queryWrapper);

        //如果没有查询到, 则返回登录失败结果
        if (employee == null) {
            return R.error(GlobalConstant.FAILURE);
        }

        //密码比对,如果不一致, 则返回登录失败结果
        if (!(employee.getPassword().equals(md5p))) {
            return R.error(GlobalConstant.FAILURE);
        }

        //查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        //1:正常,0:禁用
        if (employee.getStatus() == 0) {
            return R.error(GlobalConstant.DISABLE);
        }

        //登录成功,将员工id存入Session, 并返回登录成功结果
        request.getSession().setAttribute(GlobalConstant.EMPLOYEE_ID, employee.getId());
        //返回员工信息
        return R.success(employee);
    }

2.10 功能测试

代码实现完毕后 , 启动项目 , 访问 url , 进行登录测试。
在测试过程中, 可以通过 debug 断点调试的方式来跟踪程序的执行过程,并且可以查看程序运行时各个对象的具体赋值情况。而且需要注意, 在测试过程中,需要将所有的情况都覆盖到。

2.11 可能会出现的问题

问题说明
当我们在进行 debug 端点调试时 , 前端可能会出现如下问题 : 前端页面的控制台报出错误 - 超时 ;

解决方案

前端进行异步请求时 , 默认超时 10000ms , 可以将该值调大一些。

注意:

由于修改了JS文件,需要手动清理一下浏览器缓存,避免缓存影响,JS不能及时生效。

应该完善的操作:

登录的时候应该加入鉴权的操作

判断是否已经登陆,如果没有登录,不能访问任何界面,直接跳转到登录界面

如何实现?

加入拦截器或者过滤器,拦截从浏览器或客户端发起的请求,进行业务判断

后台系统退出功能

3. 1需求分析

在后台管理系统中,管理员或者员工,登录进入系统之后,页面跳转到后台系统首页面,此时会在系统的右上角显示当前登录用户的姓名。
如果员工需要退出系统,直接点击右侧的退出按钮即可退出系统,退出系统后页面应跳转回登录页面。

3.2 前端页面分析

A. 发起 post 请求 , 调用服务端接口 /employee/logout 执行退出操作 ;
B. 删除客户端 localStorage 存储的用户登录信息 , 跳转至登录页面 ;

3.3代码实现

需要在Controller中创建对应的处理方法, 接收页面发送的POST请求 /employee/logout ,具

体的处理逻辑:
A. 清理 Session 中的用户 id
B. 返回结果
/**
     * 退出登录
     */
    //判断前端的请求路径
    //判断前端的请求方式
    //判断前端的请求参数
    //判断返回值
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request) {
        //清理session
        request.getSession().removeAttribute(GlobalConstant.EMPLOYEE_ID);
        //返回操作成功
        return R.success(GlobalConstant.SUCCESSFUL);
    }

3.4 功能测试

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐