SpringBoot + Layui +Mybatis-plus實現簡單後臺管理系統(內建安全過濾器)
阿新 • • 發佈:2020-09-21
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"></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>
</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"></i>使用者管理
<button class="layui-btn layui-btn-xs layui-btn-normal pull-right mt10" data-type="add"><i class="layui-icon mr5"></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等)
<!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>
- 配置安全過濾器
示例工程已經添加了部分相關過濾器,詳情請瀏覽:
跨站點指令碼編制 - SpringBoot配置XSS過濾器(基於Jsoup)
SQL盲注、SQL注入 - SpringBoot配置SQL注入過濾器
跨站點請求偽造 - SpringBoot配置CSRF過濾器 - 測試使用者管理
點選選單:系統管理>使用者管理,測試模糊查詢、新增、修改,並通過新增使用者登入。