瑞吉外卖项目学习笔记:P3-后台登录退出功能开发

1.需求分析

  1. 用户名
  2. 密码
  3. 登录按钮

2.代码分析

2.1登录功能-前端页面代码

2.1.1login.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>瑞吉外卖管理端</title>
  <link rel="shortcut icon" href="../../favicon.ico">
  <!-- 引入样式 -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css">
  <link rel="stylesheet" href="../../styles/login.css">
  <link rel="stylesheet" href="../../styles/icon/iconfont.css" />
  <style>
    .body{
            
     
      min-width: 1366px;
    }
  </style>
</head> 

<body>
  <div class="login" id="login-app">
    <div class="login-box">
      <img src="../../images/login/login-l.png" alt="">
      <div class="login-form">
        <el-form ref="loginForm" :model="loginForm" :rules="loginRules" >
          <div class="login-form-title">
            <img src="../../images/login/logo.png" style="width:139px;height:42px;" alt="" />
          </div>
          <el-form-item prop="username">
            <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" maxlength="20"
              prefix-icon="iconfont icon-user" />
          </el-form-item>
          <el-form-item prop="password">
            <el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="iconfont icon-lock" maxlength="20"
              @keyup.enter.native="handleLogin" />
          </el-form-item>
          <el-form-item style="width:100%;">
            <el-button :loading="loading" class="login-btn" size="medium" type="primary" style="width:100%;"
              @click.native.prevent="handleLogin">
              <span v-if="!loading">登录</span>
              <span v-else>登录中...</span>
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>

  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="../../plugins/vue/vue.js"></script>
  <!-- 引入组件库 -->
  <script src="../../plugins/element-ui/index.js"></script>
  <!-- 引入axios -->
  <script src="../../plugins/axios/axios.min.js"></script>
  <script src="../../js/request.js"></script>
  <script src="../../js/validate.js"></script>
  <script src="../../api/login.js"></script>

  <script>
    new Vue({
            
     
      el: #login-app,
      data() {
            
     
        return {
            
     
          loginForm:{
            
     
            username: admin,
            password: 123456
          },
          loading: false
        }
      },
      computed: {
            
     
        loginRules() {
            
     
          const validateUsername = (rule, value, callback) => {
            
     
            if (value.length < 1 ) {
            
     
              callback(new Error(请输入用户名))
            } else {
            
     
              callback()
            }
          }
          const validatePassword = (rule, value, callback) => {
            
     
            if (value.length < 6) {
            
     
              callback(new Error(密码必须在6位以上))
            } else {
            
     
              callback()
            }
          }
          return {
            
     
            username: [{
            
      validator: validateUsername, trigger: blur }],
            password: [{
            
      validator: validatePassword, trigger: blur }]
          }
        }
      },
      created() {
            
     
      },
      methods: {
            
     
        async handleLogin() {
            
     
          this.$refs.loginForm.validate(async (valid) => {
            
     
            if (valid) {
            
     
              this.loading = true
              let res = await loginApi(this.loginForm)
              if (String(res.code) === 1) {
            
     
                localStorage.setItem(userInfo,JSON.stringify(res.data))
                window.location.href= /backend/index.html
              } else {
            
     
                this.$message.error(res.msg)
                this.loading = false
              }
            }
          })
        }
      }
    })
  </script>
</body>

</html>

2.1.2 login.js-登录和退出

function loginApi(data) {
          
   
  return $axios({
          
   
    url: /employee/login,
    method: post,
    data
  })
}

function logoutApi(){
          
   
  return $axios({
          
   
    url: /employee/logout,
    method: post,
  })
}

2.2登录功能开发-后端代码准备

  1. 创建数据库表对应的实体类,进行映射,以后续使用Mybatis Plus对数据库进行操作

2.2.1创建实体类Employee

package com.jq.entity;

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

/**
 * 创建数据库表对应的实体类,进行映射,以后续使用Mybatis Plus对数据库进行操作
 * 数据库表employee对应实体类Employee
 */
@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;

    private String idNumber;//身份证号码,必须在application.yaml 设置好驼峰命名

    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.2.2数据库操作-EmployeeMapper层

使用Mybatis Plus提供的BaseMapper

package com.jq.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jq.entity.Employee;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
          
   
    
}

2.2.3Service层-EmployeeService

使用Mybatis Plus提供的IService接口

package com.jq.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jq.entity.Employee;

public interface EmployeeService extends IService<Employee> {
          
   

}

2.2.4Service层-EmployeeService实现类EmployeeServiceImpl

使用Mybatis Plus提供的ServiceImpl接口

package com.jq.service.impl;


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jq.entity.Employee;
import com.jq.mapper.EmployeeMapper;
import com.jq.service.EmployeeService;
import org.springframework.stereotype.Service;

/**
 * EmployeeService实现类
 */

@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
          
   
}

2.2.5controller层-EmployeeController

package com.jq.controller;

import com.jq.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
          
   
    @Autowired
    private EmployeeService employeeService;
    
}

2.2.6返回结果封装-R

此类是一个通用类,服务端响应的所有结果都最终会包装成此种类型返回给前端页面

package com.jq.commom;

import lombok.Data;
import java.util.HashMap;
import java.util.Map;

/**
 * 此类是一个通用类,服务端响应的所有结果都最终会包装成此种类型返回给前端页面
 * @param <T>
 */
@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;
    }

}

2.3登录功能开发-后端代码方法实现

2.3.1登录方法代码

package com.jq.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jq.commom.R;
import com.jq.entity.Employee;
import com.jq.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;

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
          
   
    @Autowired
    private EmployeeService employeeService;

    /**
     * 员工登录
     * 登录方法login
     * @RequestBody 接收到的是前端传过来的JSON数据格式用到@RequestBody注解
     */
    @PostMapping("/login")
    public R<Employee>login(HttpServletRequest request, @RequestBody Employee employee){
          
   
        /**
         * 1、将页面提交的密码password进行md5加密处理
         * 2.根据页面提交的用户名username查询数据库
         * 3.如果没有查询到则返回登录失败结果
         * 4、密码比对,如果不一致则返回登录失败结果
         * 5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
         * 6、登录成功,将员工id存入Session并返回登录成功结果
         */
        //1、将页面提交的密码password进行md5加密处理
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes()); //前端传过来的密码

        //2.根据页面提交的用户名username查询数据库
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername,employee.getUsername());//查询条件
        Employee emp = employeeService.getOne(queryWrapper); //数据库查询的结果emp

        //3.如果没有查询到则返回登录失败结果
        if(emp==null){
          
   
            return R.error("登录失败");
        }
        //4、查到用户后,进行密码比对,如果不一致则返回登录失败结果
        if(!emp.getPassword().equals(password)){
          
   
            return R.error("登录失败");
        }
        //5、密码比对成功后,查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if (emp.getStatus()==0){
          
   
            return R.error("账号已禁用");
        }
        //6、用户名,密码,状态均正确,登录成功,将员工id存入Session并返回登录成功结果
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }
}

2.4退出功能开发-后端代码方法实现

/**
     * 员工退出
     * 退出功能 logout
     * employee/logout
     */
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
          
   
        //清除Session中保存的当前登录员工的id
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

2.5完善登录功能(过滤器实现)

之前的代码即使不登录,也可以直接进行访问,所需需要解决这个问题,可以使用过滤器或者拦截器

2.5.1实现步骤

  1. 创建自定义过滤器LoginCheckFilter
  2. 在启动类上加入注解@ServletComponentScan
  3. 完善过滤器的处理逻辑

2.5.2代码实现

1、获取本次请求的URI 2、判断本次请求是否需要处理 3、如果不需要处理,则直接放行 4、判断登录状态,如果已登录,则直接放行 5、如果未登录则返回未登录结果

package com.jq.filter;

import com.alibaba.fastjson.JSON;
import com.jq.commom.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义过滤器
 * 检查用户是否已经完成登录
 */
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
          
   
    //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER=new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
          
   

        HttpServletRequest request=(HttpServletRequest) servletRequest;
        HttpServletResponse response=(HttpServletResponse) servletResponse;
        /**
         * 1、获取本次请求的URI
         * 2、判断本次请求是否需要处理
         * 3、如果不需要处理,则直接放行
         * 4、判断登录状态,如果已登录,则直接放行
         * 5、如果未登录则返回未登录结果
         */
        //1、获取本次请求的URI
        String requestURI =request.getRequestURI();// /backend/index.html
        log.info("拦截到请求:{}",requestURI);
        //定义不需要处理的请求路径
        String []urls=new String[]{
          
   
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };

        //2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);
        //3、如果不需要处理,则直接放行
        if (check){
          
   
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }
        //4、判断登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("employee")!=null){
          
   
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request,response);
            return;
        }
        log.info("用户未登录");
        //5、如果未登录则返回未登录结果,通过输出流的方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));

        return;


    }

    /**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return
     */
    public boolean check(String [] urls,String requestURI){
          
   
        for (String url : urls) {
          
   
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match){
          
   
                return true;
            }
        }
        return false;
    }
}
经验分享 程序员 微信小程序 职场和发展