SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)
阅读原文时间:2023年07月09日阅读:1

1. 简介

  layui(谐音:类UI)是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到API的每一处细节都经过精心雕琢,非常适合界面的快速开发。

  (1)为服务端程序员量身定做的低门槛开箱即用的前端UI解决方案;

  (2)兼容IE6/7除外的全部浏览器;

  (3)采用经典模块化,避免工具的复杂配置,回归简单;

  (4)更多请浏览Layui官网:https://www.layui.com/

2. 初始化数据库

  创建数据库layuidemo,并初始化表结构:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_user
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_user`;
CREATE TABLE `t_sys_user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户名称',
  `nickname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户昵称',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统用户' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_user
-- ----------------------------
INSERT INTO `t_sys_user` VALUES (1, 'user', 'C3Stones', '$2a$10$WXEPqxjMwY6d6A0hkeBtGu.acRRWUOJmX7oLUuYMHF1VWWUm4EqOC');
INSERT INTO `t_sys_user` VALUES (2, 'system', '管理员', '$2a$10$dmO7Uk9/lo1D5d1SvCGgWuB050a0E2uuBDNITEpWFiIfCg.3UbA8y');

SET FOREIGN_KEY_CHECKS = 1;

3. 示例代码

  建议下载示例工程,参考搭建自己的示例工程。

  • 创建项目

  • 修改pom.xml

    http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0
    com.c3stones
    spring-boot-layui-demo
    0.0.1-SNAPSHOT
    spring-boot-layui-demo
    SpringBoot+Mybatis-Plus+Layui Demo

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.8.RELEASE</version>
        <relativePath />
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

  • 创建配置文件application.yml

    server:
    port: 8080
    servlet:
    session:
    timeout: 1800s

    spring:
    datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/layuidemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
    thymeleaf:
    prefix: classpath:/view/
    suffix: .html
    encoding: UTF-8
    servlet:
    content-type: text/html
    # 生产环境设置true
    cache: false

    Mybatis-plus配置

    mybatis-plus:
    mapper-locations: classpath:mapper/*.xml
    global-config:
    db-config:
    id-type: AUTO
    configuration:
    # 打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

    信息安全

    security:
    web:
    excludes:
    - /login
    - /logout
    - /images/**
    - /jquery/**
    - /layui/**
    xss:
    enable: true
    excludes:
    - /login
    - /logout
    - /images/*
    - /jquery/*
    - /layui/*
    sql:
    enable: true
    excludes:
    - /images/*
    - /jquery/*
    - /layui/*
    csrf:
    enable: true
    excludes:

  • 创建Mybatis-Plus配置类(配置分页)

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;

    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;

    /**

  • 创建响应实体

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;

    /**

    • 响应实体
      *

    • @author CL
      *
      */
      @Getter
      @Setter
      @AllArgsConstructor
      @NoArgsConstructor
      public class Response {

      /**

      • 响应码
        */
        private int code;

      /**

      • 响应消息体
        */
        private String msg;

      /**

      • 响应数据
        */
        private T data;

      /**

      • 失败响应
        *
      • @param msg 响应消息体
      • @return
        */
        public static Response error(String msg) {
        return new Response(500, msg, null);
        }

      /**

      • 成功响应
        *
      • @param data 响应数据
      • @return
        */
        public static Response success(T data) {
        return new Response(200, null, data);
        }

      /**

      • 成功响应
        *
      • @param msg 响应消息体
      • @return
        */
        public static Response success(String msg) {
        return new Response(200, msg, null);
        }

      /**

      • 成功响应
        *
      • @param msg 响应消息体
      • @param data 响应数据
      • @return
        */
        public static Response success(String msg, T data) {
        return new Response(200, msg, data);
        }

    }

  • 创建全局异常处理类

    import javax.servlet.http.HttpServletRequest;

    import org.springframework.boot.web.servlet.error.ErrorController;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**

    • 全局异常处理
      *

    • @author CL
      *
      */
      @Controller
      public class WebExceptionAdvice implements ErrorController {

      /**

      • 获得异常路径
        */
        @Override
        public String getErrorPath() {
        return "error";
        }

      /**

      • 异常处理,跳转到响应的页面
        *
      • @param request
      • @param model
      • @return
        */
        @RequestMapping(value = "error")
        public String handleError(HttpServletRequest request, Model model) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
        model.addAttribute("message", throwable != null ? throwable.getMessage() : null);
        switch (statusCode) {
        case 400:
        return "error/400";
        case 403:
        return "error/403";
        case 404:
        return "error/404";
        default:
        return "error/500";
        }
        }

    }

  • 创建实体

    import java.io.Serializable;

    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonIgnore;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.NoArgsConstructor;

    /**

    }

  • 创建Mapper

    import org.apache.ibatis.annotations.Mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.c3stones.entity.User;

    /**

    • 系统用户Mapper
      *
    • @author CL
      *
      */
      @Mapper
      public interface UserMapper extends BaseMapper {

    }

  • 创建Service

    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.c3stones.entity.User;

    /**

    • 系统用户Service
      *

    • @author CL
      *
      */
      public interface UserService extends IService {

      /**

      • 查询列表数据
        *
      • @param user 系统用户
      • @param current 当前页
      • @param size 每页显示条数
      • @return
        */
        public Page listData(User user, long current, long size);

      /**

      • 检验用户名称是否唯一
        *
      • @param userName 用户名称
      • @return
        */
        public Boolean checkUserName(String userName);

    }

  • 创建Service实现

    import org.springframework.stereotype.Service;

    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.c3stones.entity.User;
    import com.c3stones.mapper.UserMapper;
    import com.c3stones.service.UserService;

    import cn.hutool.core.util.StrUtil;

    /**

    • 系统用户Service实现
      *

    • @author CL
      *
      */
      @Service
      public class UserServiceImpl extends ServiceImpl implements UserService {

      /**

      • 查询列表数据
        *
      • @param user 系统用户
      • @param current 当前页
      • @param size 每页显示条数
      • @return
        */
        @Override
        public Page listData(User user, long current, long size) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        if (null != user.getId()) {
        queryWrapper.eq("id", user.getId());
        }
        if (StrUtil.isNotBlank(user.getUsername())) {
        queryWrapper.like("username", user.getUsername());
        }
        if (StrUtil.isNotBlank(user.getNickname())) {
        queryWrapper.like("nickname", user.getNickname());
        }
        return baseMapper.selectPage(new Page<>(current, size), queryWrapper);
        }

      /**

      • 检验用户名称是否唯一
        *
      • @param userName 用户名称
      • @return
        */
        @Override
        public Boolean checkUserName(String userName) {
        if (StrUtil.isNotBlank(userName)) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.like("username", userName);
        Integer count = baseMapper.selectCount(queryWrapper);
        return (count != null && count > 0) ? false : true;
        }
        return false;
        }

    }

  • 创建登录Controller

    import javax.servlet.http.HttpSession;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.ResponseBody;

    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.c3stones.common.Response;
    import com.c3stones.entity.User;
    import com.c3stones.service.UserService;

    import cn.hutool.core.util.StrUtil;
    import cn.hutool.crypto.digest.BCrypt;

    /**

    • 系统登录Controller
      *

    • @author CL
      *
      */
      @Controller
      public class LoginController {

      @Autowired
      private UserService userService;

      /**

      • 登录页
        *
      • @return
        */
        @GetMapping(value = { "login", "" })
        public String login() {
        return "login";
        }

      /***

      • 登录验证
        *
      • @param user 系统用户
      • @return
        */
        @PostMapping(value = "login")
        @ResponseBody
        public Response login(User user, HttpSession session) {
        if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())) {
        return Response.error("用户名或密码不能为空");
        }
        User queryUser = new User();
        queryUser.setUsername(user.getUsername());
        queryUser = userService.getOne(new QueryWrapper<>(queryUser));
        if (queryUser == null || !StrUtil.equals(queryUser.getUsername(), user.getUsername())
        || !BCrypt.checkpw(user.getPassword(), queryUser.getPassword())) {
        return Response.error("用户名或密码错误");
        }
        session.setAttribute("user", queryUser);
        return Response.success("登录成功", queryUser);
        }

      /**

      • 登出
        *
      • @param httpSession
      • @return
        */
        @GetMapping(value = "logout")
        public String logout(HttpSession httpSession) {
        httpSession.invalidate();
        return "redirect:/login";
        }

    }

  • 创建登录拦截器

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;

    /**

    • 登录拦截器
      *

    • @author CL
      *
      */
      @Component
      public class LoginInterceptor implements HandlerInterceptor {

      /**

      • 拦截处理
        */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        Object user = request.getSession().getAttribute("user");
        if (null == user) {
        response.sendRedirect("/login");
        }
        return true;
        }

    }

  • 配置登录拦截器

    import java.util.List;

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    import lombok.Setter;

    /**

    • Web配置类
      *

    • @author CL
      *
      */
      @Configuration
      @ConfigurationProperties(prefix = "security.web")
      public class WebConfigurer implements WebMvcConfigurer {

      /**

      • 忽略的URL
        */
        @Setter
        private List excludes;

      /**

      • 配置拦截器
        / @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/*").excludePathPatterns(excludes);
        }

    }

  • 创建首页Contrller

    import javax.servlet.http.HttpSession;

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;

    /**

    • 系统首页Controller
      *

    • @author CL
      *
      */
      @Controller
      public class IndexController {

      /**

      • 首页
        *
      • @return
        */
        @GetMapping(value = "index")
        public String index(Model model, HttpSession httpSession) {
        model.addAttribute("user", httpSession.getAttribute("user"));
        return "index";
        }

      /**

      • 控制台
        *
      • @return
        */
        @GetMapping(value = "view")
        public String view() {
        return "pages/view";
        }

    }

  • 创建启动类

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    /**

    • 启动类
      *

    • @author CL
      *
      */
      @SpringBootApplication
      public class Application {

      public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
      }

    }

  • 拷贝静态资源

      将示例工程的resource目录下的static文件夹及其子文件拷贝到本工程对应文件夹下。

  • 创建前端页面文件夹

      在resource目录下创建view文件夹,工程的所有页面都会写在此文件夹下(和配置文件中的spring.thymeleaf.prefix对应)。

  • 创建登录页面


    C3Stones

  • 创建系统框架页面,并初始化菜单



    C3Stones

        <div class="layui-side custom-admin">
            <div class="layui-side-scroll">
                <div class="custom-logo">
                    <img alt="" th:src="@{/images/logo.jpg}">
                    <h1>C3Stones</h1>
                </div>
                <ul id="Nav" class="layui-nav layui-nav-tree">
                    <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon"></i>
                            <em>主页</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a th:href="@{/view}">控制台</a></dd>
                        </dl>
                    </li>
                    <li class="layui-nav-item">
                        <a href="javascript:;">
                            <i class="layui-icon"></i>
                            <em>系统管理</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a th:href="@{/user/list}">用户管理</a></dd>
                        </dl>
                    </li>
                </ul>
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;div class="layui-body"&gt;
         &lt;div class="layui-tab app-container" lay-allowClose="true" lay-filter="tabs"&gt;
            &lt;ul id="appTabs" class="layui-tab-title custom-tab"&gt;&lt;/ul&gt;
            &lt;div id="appTabPage" class="layui-tab-content"&gt;&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;div class="layui-footer"&gt;
        &lt;p&gt; 2020 - C3Stones Blog : &lt;a href="https://www.cnblogs.com/cao-lei/" target="_blank"&gt;https://www.cnblogs.com/cao-lei/&lt;/a&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="mobile-mask"&gt;&lt;/div&gt;
    </div>


  • 创建控制台页面

      在view文件夹下创建pages文件夹。


    欢迎使用

  • 登录测试

      浏览器访问:http://127.0.0.1:8080/,输入用户名密码:user/123456,或者system/123456,进行测试正常用户登录,并测试用户不存在、密码错误等异常。

  • 创建用户Controller

    import javax.validation.constraints.NotNull;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.util.Assert;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;

    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.c3stones.common.Response;
    import com.c3stones.entity.User;
    import com.c3stones.service.UserService;

    import cn.hutool.crypto.digest.BCrypt;

    /**

    • 系统用户Controller
      *

    • @author CL
      *
      */
      @Controller
      @RequestMapping(value = "user")
      public class UserController {

      @Autowired
      private UserService userService;

      /**

      /**

      /**

      • 删除
        *
      • @param user 系统用户
      • @return
        */
        @RequestMapping(value = "delete")
        @ResponseBody
        public Response delete(User user) {
        Assert.notNull(user.getId(), "ID不能为空");
        boolean result = userService.removeById(user.getId());
        return Response.success(result);
        }

      /**

      • 修改
        *
      • @param user 系统用户
      • @param model
      • @return
        */
        @RequestMapping(value = "edit")
        public String edit(User user, Model model) {
        Assert.notNull(user.getId(), "ID不能为空");
        model.addAttribute("user", userService.getById(user.getId()));
        return "pages/userEdit";
        }

      /**

      • 检验用户名称是否唯一
        *
      • @param userName 用户名称
      • @return
        */
        @RequestMapping(value = "check")
        @ResponseBody
        public Response checkUserName(@NotNull String username) {
        Boolean checkResult = userService.checkUserName(username);
        return Response.success(checkResult);
        }

      /**

      • 更新
        *
      • @param user 系统用户
      • @return
        */
        @RequestMapping(value = "update")
        @ResponseBody
        public Response update(User user) {
        Assert.notNull(user.getId(), "ID不能为空");
        boolean result = userService.updateById(user);
        return Response.success(result);
        }

      /**

      /**

      • 保存
        *
      • @param user 系统用户
      • @return
        */
        @RequestMapping(value = "save")
        @ResponseBody
        public Response save(User user) {
        user.setPassword(BCrypt.hashpw(user.getPassword()));
        boolean result = userService.save(user);
        return Response.success(result);
        }

    }

  • 创建用户列表及新增、编辑页面




    用户管理
    用户ID:
    用户名称:
    用户昵称:




    用户昵称
    请输入6-8位密码,且只能包含字母或数字
    确认密码




    用户昵称

  • 创建全局异常页面(404、500等)

      在view文件夹下创建error文件夹,并创建404.html、500.html等。


    C3Stones


    C3Stones

  • 配置安全过滤器

      示例工程已经添加了部分相关过滤器,详情请浏览:

      跨站点脚本编制 - SpringBoot配置XSS过滤器(基于Jsoup)

      SQL盲注、SQL注入 - SpringBoot配置SQL注入过滤器

      跨站点请求伪造 - SpringBoot配置CSRF过滤器

  • 测试用户管理

      点击菜单:系统管理>用户管理,测试模糊查询、新增、修改,并通过新增用户登录。

4. 项目地址

  spring-boot-layui-demo

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章