SpringBoot + Layui +Mybatis-plus實現簡單后台管理系統(內置安全過濾器)


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
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.c3stones</groupId>
	<artifactId>spring-boot-layui-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-layui-demo</name>
	<description>SpringBoot+Mybatis-Plus+Layui Demo</description>

	<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>

</project>
  • 創建配置文件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;

/**
 * Mybatis-Plus配置類
 * 
 * @author CL
 *
 */
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisPlusConfig {

	/**
	 * 注入分頁插件
	 */
	@Bean
	public PaginationInterceptor paginationInterceptor() {
		return new PaginationInterceptor();
	}
}
  • 創建響應實體
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * 響應實體
 * 
 * @author CL
 *
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Response<T> {

	/**
	 * 響應碼
	 */
	private int code;

	/**
	 * 響應消息體
	 */
	private String msg;

	/**
	 * 響應數據
	 */
	private T data;

	/**
	 * 失敗響應
	 * 
	 * @param msg 響應消息體
	 * @return
	 */
	public static <T> Response<T> error(String msg) {
		return new Response<T>(500, msg, null);
	}

	/**
	 * 成功響應
	 * 
	 * @param data 響應數據
	 * @return
	 */
	public static <T> Response<T> success(T data) {
		return new Response<T>(200, null, data);
	}

	/**
	 * 成功響應
	 * 
	 * @param msg 響應消息體
	 * @return
	 */
	public static <T> Response<T> success(String msg) {
		return new Response<T>(200, msg, null);
	}

	/**
	 * 成功響應
	 * 
	 * @param msg  響應消息體
	 * @param data 響應數據
	 * @return
	 */
	public static <T> Response<T> success(String msg, T data) {
		return new Response<T>(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;

/**
 * 系統用戶信息
 * 
 * @author CL
 *
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_sys_user")
@EqualsAndHashCode(callSuper = false)
public class User implements Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * 用戶ID
	 */
	@TableId(type = IdType.AUTO)
	private Integer id;

	/**
	 * 用戶名稱
	 */
	private String username;

	/**
	 * 用戶昵稱
	 */
	private String nickname;

	/**
	 * 用戶密碼
	 */
	@JsonIgnore
	private String password;

}
  • 創建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<User> {

}
  • 創建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<User> {

	/**
	 * 查詢列表數據
	 * 
	 * @param user    系統用戶
	 * @param current 當前頁
	 * @param size    每頁顯示條數
	 * @return
	 */
	public Page<User> 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<UserMapper, User> implements UserService {

	/**
	 * 查詢列表數據
	 * 
	 * @param user    系統用戶
	 * @param current 當前頁
	 * @param size    每頁顯示條數
	 * @return
	 */
	@Override
	public Page<User> listData(User user, long current, long size) {
		QueryWrapper<User> 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<User> 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<User> 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<String> 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對應)。
  • 創建登錄頁面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>C3Stones</title>
    <link th:href="@{/images/favicon.ico}" rel="icon">
	<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
	<link th:href="@{/layui/css/login.css}" rel="stylesheet" />
	<link th:href="@{/layui/css/view.css}" rel="stylesheet" />
	<script th:src="@{/layui/layui.all.js}"></script>
	<script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
</head>
<body class="login-wrap">
    <div class="login-container">
        <form class="login-form">
        	<div class="input-group text-center text-gray">
        		<h2>歡迎登錄</h2>
        	</div>
            <div class="input-group">
                <input type="text" id="username" class="input-field">
                <label for="username" class="input-label">
                    <span class="label-title">用戶名</span>
                </label>
            </div>
            <div class="input-group">
                <input type="password" id="password" class="input-field">
                <label for="password" class="input-label">
                    <span class="label-title">密碼</span>
                </label>
            </div>
            <button type="button" class="login-button">登錄<i class="ai ai-enter"></i></button>
        </form>
    </div>
</body>
</html>
<script>
layui.define(['element'],function(exports){
    var $ = layui.$;
    $('.input-field').on('change',function(){
        var $this = $(this),
            value = $.trim($this.val()),
            $parent = $this.parent();
        if(!isEmpty(value)){
            $parent.addClass('field-focus');
        }else{
            $parent.removeClass('field-focus');
        }
    })
    exports('login');
});

// 登錄
var layer = layui.layer;
$(".login-button").click(function() {
	var username = $("#username").val();
	var password = $("#password").val();
	if (isEmpty(username) || isEmpty(password)) {
		layer.msg("用戶名或密碼不能為空", {icon: 2});
		return ;
	}
	
	var loading = layer.load(1, {shade: [0.3, '#fff']});
	$.ajax({
        url : "[[@{/}]]login",
        data : {username : username, password : password},
        type : "post",
        dataType : "json",
        error : function(data) {
        },
        success : function(data) {
        	layer.close(loading);
        	if (data.code == 200) {
        		location.href = "[[@{/}]]index";
        	} else {
        		layer.msg(data.msg, {icon: 2});
        	}
        }
	});
});

function isEmpty(n) {
	if (n == null || n == '' || typeof(n) == 'undefined') {
		return true;
	}
	return false;
}
</script>
  • 創建系統框架頁面,並初始化菜單
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>C3Stones</title>
    <link th:href="@{/images/favicon.ico}" rel="icon">
	<link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
	<link th:href="@{/layui/css/admin.css}" rel="stylesheet" />
	<script th:src="@{/layui/layui.js}"></script>
	<script th:src="@{/layui/js/index.js}" data-main="home"></script>
</head>
<body class="layui-layout-body">
    <div class="layui-layout layui-layout-admin">
        <div class="layui-header custom-header">
            <ul class="layui-nav layui-layout-left">
                <li class="layui-nav-item slide-sidebar" lay-unselect>
                    <a href="javascript:;" class="icon-font"><i class="ai ai-menufold"></i></a>
                </li>
            </ul>
            <ul class="layui-nav layui-layout-right">
                <li class="layui-nav-item">
                    <a href="javascript:;">[[${user?.nickname}]]</a>
                    <dl class="layui-nav-child">
                        <dd><a th:href="@{/logout}">退出</a></dd>
                    </dl>
                </li>
            </ul>
        </div>

        <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">&#xe68e;</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">&#xe716;</i>
                            <em>系統管理</em>
                        </a>
                        <dl class="layui-nav-child">
                            <dd><a th:href="@{/user/list}">用戶管理</a></dd>
                        </dl>
                    </li>
                </ul>

            </div>
        </div>

        <div class="layui-body">
             <div class="layui-tab app-container" lay-allowClose="true" lay-filter="tabs">
                <ul id="appTabs" class="layui-tab-title custom-tab"></ul>
                <div id="appTabPage" class="layui-tab-content"></div>
            </div>
        </div>

        <div class="layui-footer">
            <p>© 2020 - C3Stones Blog : <a href="https://www.cnblogs.com/cao-lei/" target="_blank">https://www.cnblogs.com/cao-lei/</a></p>
        </div>
        <div class="mobile-mask"></div>
    </div>
</body>
</html>
  • 創建控制台頁面
      在view文件夾下創建pages文件夾。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
    <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
    <script th:src="@{/layui/layui.all.js}"></script>
    <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
    <title></title>
</head>
<body class="layui-view-body">
	<div class="layui-row" style="text-align: center;">
		<div class="layui-col-md12" style="padding: 18% 0px 20px 0px;">
			<font class="layui-text"><h1>歡迎使用</h1></font>
		</div>
	</div>
</body>
</html>
  • 登錄測試
      瀏覽器訪問: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;

	/**
	 * 查詢列表
	 * 
	 * @return
	 */
	@RequestMapping(value = "list")
	public String list() {
		return "pages/userList";
	}

	/**
	 * 查詢列表數據
	 * 
	 * @param user    系統用戶
	 * @param current 當前頁
	 * @param size    每頁顯示條數
	 * @return
	 */
	@RequestMapping(value = "listData")
	@ResponseBody
	public Response<Page<User>> listData(User user, @RequestParam(name = "page") long current,
			@RequestParam(name = "limit") long size) {
		Page<User> page = userService.listData(user, current, size);
		return Response.success(page);
	}

	/**
	 * 刪除
	 * 
	 * @param user 系統用戶
	 * @return
	 */
	@RequestMapping(value = "delete")
	@ResponseBody
	public Response<Boolean> 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<Boolean> checkUserName(@NotNull String username) {
		Boolean checkResult = userService.checkUserName(username);
		return Response.success(checkResult);
	}

	/**
	 * 更新
	 * 
	 * @param user 系統用戶
	 * @return
	 */
	@RequestMapping(value = "update")
	@ResponseBody
	public Response<Boolean> update(User user) {
		Assert.notNull(user.getId(), "ID不能為空");
		boolean result = userService.updateById(user);
		return Response.success(result);
	}

	/**
	 * 新增
	 * 
	 * @return
	 */
	@RequestMapping(value = "add")
	public String add() {
		return "pages/userAdd";
	}
	
	/**
	 * 保存
	 * 
	 * @param user 系統用戶
	 * @return
	 */
	@RequestMapping(value = "save")
	@ResponseBody
	public Response<Boolean> save(User user) {
		user.setPassword(BCrypt.hashpw(user.getPassword()));
		boolean result = userService.save(user);
		return Response.success(result);
	}

}
  • 創建用戶列表及新增、編輯頁面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
    <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
    <script th:src="@{/layui/layui.all.js}"></script>
    <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
    <title></title>
</head>
<body class="layui-view-body">
	<div class="layui-content">
	    <div class="layui-row">
			<div class="layui-card">
                <div class="layui-card-header">
                	<i class="layui-icon mr5">&#xe66f;</i>用戶管理
                	<button class="layui-btn layui-btn-xs layui-btn-normal pull-right mt10" data-type="add"><i class="layui-icon mr5">&#xe654;</i>新增</button>	
                </div>
                <div class="layui-card-body">
                	<div class="searchTable">
					 用戶ID:
					 <div class="layui-inline mr5">
					 	<input class="layui-input" name="id" autocomplete="off">
					 </div>
					 用戶名稱:
					 <div class="layui-inline mr5">
					 	<input class="layui-input" name="username" autocomplete="off">
					 </div>
					 用戶昵稱:
					 <div class="layui-inline mr10">
					 	<input class="layui-input" name="nickname" autocomplete="off">
					 </div>
					 <button class="layui-btn" data-type="reload">查詢</button>
					 <button class="layui-btn layui-btn-primary" data-type="reset">重置</button>
					</div>
                	<table class="layui-hide" id="userDataTable" lay-filter="config"></table>
					<script type="text/html" id="operation">
						<a class="layui-btn layui-btn-xs" lay-event="edit">修改</a>
						<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
					</script>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
var element = layui.element;
var table = layui.table;
var layer = layui.layer;
table.render({
	id: 'userTable'
	,elem: '#userDataTable'
    ,url: '[[@{/user/listData}]]'
   	,page: {
  		layout: ['prev', 'page', 'next', 'count', 'skip', 'limit']
  	    ,groups: 5
  	    ,first: false
  	    ,last: false
	}
    ,cols: [
    	[
	      {field:'id', width: 50, title: 'ID'}
	      ,{field:'username', title: '用戶名稱', align: 'center'}
	      ,{field:'nickname', title: '用戶昵稱', align: 'center'}
	      ,{title:'操作', align: 'center', toolbar: '#operation', width:150}
    	]
   	]
    ,response: {
        statusCode: 200
    }
    ,parseData: function(res){
    	return {
    		"code": res.code
            ,"msg": res.msg
            ,"count": res.data.total
            ,"data": res.data.records
    	};
    }
});

active = {
	add: function() {
		layer.open({
    		type: 2,
    		area: ['80%', '80%'],
    		title: '新增',
    		content: '[[@{/}]]user/add'
    	});
	},
	reload: function() {
		table.reload('userTable', {
			page: {
				curr: 1
			}
			,where: {
				id : $("input[name='id']").val()
				,username : $("input[name='username']").val()
				,nickname : $("input[name='nickname']").val()
			}
		}, 'data');
	},
	reset: function() {
		$(".layui-input").val("");
	}
};

// 按鈕事件
$('.layui-btn').on('click', function(){
    var type = $(this).data('type');
    active[type] ? active[type].call(this) : '';
});

//監聽行工具事件
table.on('tool(config)', function(obj){
	var row = obj.data;
	if(obj.event === 'del') {
		layer.confirm("確認刪除嗎?", {icon: 3, title:'提示'}, function(index) {
			layer.close(index);
			$.ajax({
		        url : "[[@{/}]]user/delete",
		        data : {'id': row.id},
		        type : "post",
		        dataType : "json",
		        error : function(data) {
		        	errorHandle(data);
		        },
		        success : function(data) {
		        	$(".searchTable .layui-btn").eq(0).click();
		        }
		    });
		});
    } else if (obj.event === 'edit') {
    	layer.open({
    		type: 2,
    		area: ['80%', '80%'],
    		title: '修改',
    		content: '[[@{/}]]user/edit?id=' + row.id
    	});
    }
});

//錯誤處理
function errorHandle(data) {
	if (data.status == 404) {
		layer.msg("請求資源不存在", {icon: 2});
	} else {
		layer.msg("服務端異常", {icon: 2});
	}
	return;
}
</script>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
    <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
    <script th:src="@{/layui/layui.all.js}"></script>
    <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
    <script th:src="@{/jquery/jquery-form.js}"></script>
    <title></title>
</head>
<body class="layui-view-body">
	<div class="layui-row">
    	<div class="layui-card">
        	<form class="layui-form layui-card-body layui-form-pane" method="post" th:action="@{/user/save}">
				<div class="layui-form-item">
					<label class="layui-form-label"><i>*</i>用戶名稱</label>
					<div class="layui-input-block">
						<input type="text" name="username" lay-verify="username" placeholder="6-8位英文字母" maxlength="8" autocomplete="off" class="layui-input">
					</div>
				</div>
				<div class="layui-form-item">
					<label class="layui-form-label"><i>*</i>用戶昵稱</label>
					<div class="layui-input-block">
						<input type="text" name="nickname" lay-verify="required" maxlength="15" autocomplete="off" class="layui-input">
					</div>
				</div>
				<div class="layui-form-item">
    				<label class="layui-form-label"><i>*</i>用戶密碼</label>
					<div class="layui-input-inline">
						<input type="password" name="password" required lay-verify="password" maxlength="8" autocomplete="off" class="layui-input">
	    			</div>
					<div class="layui-form-mid layui-word-aux">請輸入6-8位密碼,且只能包含字母或數字</div>
  				</div>
				<div class="layui-form-item">
					<label class="layui-form-label"><i>*</i>確認密碼</label>
					<div class="layui-input-inline">
						<input type="password" name="confirmPwd" lay-verify="confirm" maxlength="8" autocomplete="off" class="layui-input">
	    			</div>
				</div>
				<div class="layui-form-item">
	                <button type="submit" class="layui-btn" lay-submit lay-filter="*">提交</button>
	                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
              	</div>
			</form>
		</div>
	</div>
</body>
<script>
var form = layui.form;
var layer = layui.layer;

// 自定義檢驗
form.verify({
	username: function(val) {
		if (isEmpty(val)) {
			return '必填項不能為空';
		}
		debugger;
		var reg = /^[A-Za-z]{6,8}$/;
		if (!reg.test(val)) {
			return '用戶名稱不合法';
		}
		if (!checkUsername(val)) {
			return '用戶名稱已存在';
		}
	},
	password: [
		/^[A-Za-z0-9]{6,8}$/
	    ,'請輸入6-8位密碼,且只能包含字母或數字'
	],
	confirm: function(val) {
		if (isEmpty(val)) {
			return '必填項不能為空';
		}
		if (val != $("input[name='password']").val()) {
			return '確認密碼與用戶密碼不一致';
		}
	}
});

// 檢測用戶名稱是否唯一
function checkUsername(username) {
	var checkResult = true;
	$.ajax({
        url : "[[@{/}]]user/check",
        data : {'username': username},
        type : "post",
        dataType : "json",
        async: false,
        error : function(data) {
        	errorHandle(data);
        },
        success : function(data) {
        	checkResult =  data.data;
        }
    });
	return checkResult;
}

// 提交表單
form.on('submit(*)', function(data){
	$(".layui-form").ajaxForm({
		error: function(data){
			errorHandle(data);
		},
		success: function(data) {
			parent.location.reload();
			var index = parent.layer.getFrameIndex(window.name);
			parent.layer.close(index);
		}
	});
});

//是否為空
function isEmpty(n) {
	if (n == null || n == '' || typeof(n) == 'undefined') {
		return true;
	}
	return false;
}

// 錯誤處理
function errorHandle(data) {
	if (data.status == 404) {
		layer.msg("請求資源不存在", {icon: 2});
	} else {
		layer.msg("服務端異常", {icon: 2});
	}
	return;
}
</script>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <link th:href="@{/layui/css/layui.css}" rel="stylesheet" />
    <link th:href="@{/layui/css/view.css}" rel="stylesheet" />
    <script th:src="@{/layui/layui.all.js}"></script>
    <script th:src="@{/jquery/jquery-2.1.4.min.js}"></script>
    <script th:src="@{/jquery/jquery-form.js}"></script>
    <title></title>
</head>
<body class="layui-view-body">
	<div class="layui-row">
    	<div class="layui-card">
        	<form class="layui-form layui-card-body layui-form-pane" method="post" th:action="@{/user/update}">
            	<div class="layui-form-item">
					<label class="layui-form-label">用戶ID</label>
					<div class="layui-input-block">
						<input type="text" name="id" th:value="${user?.id}" readonly="readonly" class="layui-input readonly">
					</div>
				</div>
				<div class="layui-form-item">
					<label class="layui-form-label"><i>*</i>用戶名稱</label>
					<div class="layui-input-block">
						<input type="text" name="username" th:value="${user?.username}" lay-verify="username" placeholder="6-8位英文字母" maxlength="8" autocomplete="off" class="layui-input">
					</div>
				</div>
				<div class="layui-form-item">
					<label class="layui-form-label"><i>*</i>用戶昵稱</label>
					<div class="layui-input-block">
						<input type="text" name="nickname" th:value="${user?.nickname}" lay-verify="required" maxlength="15" lay-reqtext="用戶昵稱是必填項" autocomplete="off" class="layui-input">
					</div>
				</div>
				<div class="layui-form-item">
	                <button type="submit" class="layui-btn" lay-submit lay-filter="*">提交</button>
	                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
              	</div>
			</form>
		</div>
	</div>
</body>
<script>
var form = layui.form;
var layer = layui.layer;

// 自定義檢驗
form.verify({
	username: function(val) {
		if (isEmpty(val)) {
			return '必填項不能為空';
		}
		if (val != '[[${user?.username}]]') {
			var reg = /^[A-Za-z]{6,8}$/;
			if (!reg.test(val)) {
				return '用戶名稱不合法';
			}
			if (!checkUsername(val)) {
				return '用戶名稱已存在';
			}
		}
		
	}
});

// 檢測用戶名稱是否唯一
function checkUsername(username) {
	var checkResult = true;
	$.ajax({
        url : "[[@{/}]]user/check",
        data : {'username': username},
        type : "post",
        dataType : "json",
        async: false,
        error : function(data) {
        	errorHandle(data);
        },
        success : function(data) {
        	checkResult =  data.data;
        }
    });
	return checkResult;
}

// 提交表單
form.on('submit(*)', function(data){
	$(".layui-form").ajaxForm({
		error: function(data){
			errorHandle(data);
		},
		success: function(data) {
			parent.location.reload();
			var index = parent.layer.getFrameIndex(window.name);
			parent.layer.close(index);
		}
	});
});

// 是否為空
function isEmpty(n) {
	if (n == null || n == '' || typeof(n) == 'undefined') {
		return true;
	}
	return false;
}

// 錯誤處理
function errorHandle(data) {
	if (data.status == 404) {
		layer.msg("請求資源不存在", {icon: 2});
	} else {
		layer.msg("服務端異常", {icon: 2});
	}
	return;
}
</script>
</html>
  • 創建全局異常頁面(404、500等)
      在view文件夾下創建error文件夾,並創建404.html、500.html等。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>C3Stones</title>
    <link th:href="@{/images/favicon.ico}" rel="icon">
    <style>
    	body {
    		height: 300px;
    		background: url("/images/404.png") center no-repeat;
    	}
    </style>
</head>
<body class="layui-layout-body">
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>C3Stones</title>
    <link th:href="@{/images/favicon.ico}" rel="icon">
    <style>
    	body {
    		height: 300px;
    		background: url("/images/500.png") center no-repeat;
    	}
    </style>
</head>
<body class="layui-layout-body">
</body>
</html>

4. 項目地址

  spring-boot-layui-demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM