制作基于springboot的简易学生管理系统(详细)

  • 基于书本与百度创作,内容简易,请多多指教( ̄▽ ̄)/
  • 设计一个简易学生管理系统
  • 所需环境
  • 创建一个springboot项目
  • 设计数据库
  • 配置Gradle
  • 配置SpringBoot
  • 项目结构
  • 创建实体类
  • 前端页面
    • 模板页面,用于引用,减少重复代码
    • 普通页面
    • JavaScript
  • 控制层
  • 服务层
  • 数据访问层(Dao,Data Access Object)
  • SpringSecurity 配置
  • 跑系统

基于书本与百度创作,内容简易,请多多指教( ̄▽ ̄)/

设计一个简易学生管理系统

所需环境

Java 1.8(JDK8)
MySQL 8
Gradle 6.3

创建一个springboot项目

可视化速度创建(https://start.spring.io/)
一览无遗、一看就懂的界面,注意左边的选项即可,右边的可以在日后自行在build.gradle文件里添加:

点击“GENERATE”生成并下载到本地:

解压并在项目根目录下执行“gradle build”命令来编译:

现在,在项目根目录的下的“build”文件夹下的“libs”文件夹内执行“java -jar demo-0.0.1-SNAPSHOT.jar”命令:

可见使用端口为8080,打开浏览器输入“localhost:8080”查看,由于使用了Spring Security,所以一开始就有登录界面,用户名固定为“user”、密码初次使用随机生成,在上面的命令行内找,这里就不演示登录了:

到此,一个简单的springboot项目就可以上线了,虽然还没有任何内容。

设计数据库

一览无遗,这里简单设计,不复杂。

CREATE TABLE `newtest`.`authority` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = InnoDB
AVG_ROW_LENGTH = 0
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci
KEY_BLOCK_SIZE = 0
MAX_ROWS = 0
MIN_ROWS = 0
ROW_FORMAT = Compact;CREATE TABLE `newtest`.`course` (
`courseId` int NOT NULL COMMENT '课程ID',
`courseName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '课程名',
`time` date NOT NULL COMMENT '开课时期',
PRIMARY KEY (`courseId`)
)
ENGINE = InnoDB
AVG_ROW_LENGTH = 0
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
KEY_BLOCK_SIZE = 0
MAX_ROWS = 0
MIN_ROWS = 0
ROW_FORMAT = Dynamic;CREATE TABLE `newtest`.`score` (
`userId` int NOT NULL,
`courseId` int NOT NULL,
`score` int NOT NULL,
PRIMARY KEY (`userId`, `courseId`) ,
CONSTRAINT `score-courseId` FOREIGN KEY (`courseId`) REFERENCES `newtest`.`course` (`courseId`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `score-userId` FOREIGN KEY (`userId`) REFERENCES `newtest`.`user` (`userId`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `score-courseId` (`courseId` ASC) USING BTREE
)
ENGINE = InnoDB
AVG_ROW_LENGTH = 0
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci
KEY_BLOCK_SIZE = 0
MAX_ROWS = 0
MIN_ROWS = 0
ROW_FORMAT = Dynamic;CREATE TABLE `newtest`.`user` (
`userId` int NOT NULL AUTO_INCREMENT,
`userName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`imgURL` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`userId`)
)
ENGINE = InnoDB
AVG_ROW_LENGTH = 0
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci
KEY_BLOCK_SIZE = 0
MAX_ROWS = 0
MIN_ROWS = 0
ROW_FORMAT = Dynamic;CREATE TABLE `newtest`.`userauthority` (
`id` int NOT NULL AUTO_INCREMENT,
`userId` int NOT NULL,
`authorityId` int NOT NULL,
PRIMARY KEY (`id`) ,
CONSTRAINT `userAuthority-authorityId` FOREIGN KEY (`authorityId`) REFERENCES `newtest`.`authority` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `userAuthority-userId` FOREIGN KEY (`userId`) REFERENCES `newtest`.`user` (`userId`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `userAuthority-userId` (`userId` ASC) USING BTREE,
INDEX `userAuthority-authorityId` (`authorityId` ASC) USING BTREE
)
ENGINE = InnoDB
AVG_ROW_LENGTH = 0
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci
KEY_BLOCK_SIZE = 0
MAX_ROWS = 0
MIN_ROWS = 0
ROW_FORMAT = Dynamic;

配置Gradle

都是基本需求,无特殊设置,顾名思义。对于该配置可以更进一步,详情很多,自行百度。
build.gradle

plugins {id 'org.springframework.boot' version '2.2.6.RELEASE'id 'io.spring.dependency-management' version '1.0.9.RELEASE'id 'java'id 'idea'
}group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'configurations {developmentOnlyruntimeClasspath {extendsFrom developmentOnly}compileOnly {extendsFrom annotationProcessor}
}repositories {mavenLocal()maven { url 'http://maven.aliyun.com/nexus/content/repositories/central/' }mavenCentral()
}dependencies {implementation 'org.springframework.boot:spring-boot-starter-security'implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'implementation 'org.springframework.boot:spring-boot-starter-web'implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.2'compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5', version: '3.0.4.RELEASE'compile group: 'com.alibaba', name: 'fastjson', version: '1.2.68'compile group: 'org.apache.poi', name: 'poi', version: '4.1.2'compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.2'developmentOnly 'org.springframework.boot:spring-boot-devtools'runtimeOnly 'mysql:mysql-connector-java'annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'testImplementation('org.springframework.boot:spring-boot-starter-test') {exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'}testImplementation 'org.springframework.security:spring-security-test'
}test {useJUnitPlatform()
}

配置SpringBoot

都是基本需求,无特殊设置,顾名思义。对于该配置可以更进一步,详情很多,自行百度。
application.properties

#Thymeleaf编码
spring.thymeleaf.encoding=UTF-8
#热部署静态文件
spring.thymeleaf.cache=false
#使用HTML5标准
spring.thymeleaf.mode=HTML5
#数据库驱动设置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接设置
spring.datasource.url=jdbc:mysql://localhost:3306/newTest?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

项目结构

创建实体类

  1. User.java
  2. Authority.java
  3. UserAuthorities.java
  4. ResultMsg.java
  5. Page.java
  6. ExcelUtils.java

User
实现UserDetails类使其可以用于SpringSecurity的认证返回对象。

package com.example.demo.entity;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** 用户实体类** @author Administrator*/
public class User implements UserDetails {private long userId;private String userName;private String password;private String imgURL;private List<Authority> authorities = new ArrayList<Authority>();private boolean isAccountNonExpired = true;private boolean isAccountNonLocked = true;private boolean isCredentialsNonExpired = true;private boolean isEnabled = true;public User() {}public User(String userName, String password) {BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();this.password = bCryptPasswordEncoder.encode(password);this.userName = userName;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}public void setAuthorities(List<Authority> authorities) {this.authorities = authorities;}@Overridepublic String getPassword() {return this.password;}public void setPassword(String password) {this.password = password;}@Overridepublic String getUsername() {return this.userName;}@Overridepublic boolean isAccountNonExpired() {return this.isAccountNonExpired;}public void setAccountNonExpired(boolean accountNonExpired) {this.isAccountNonExpired = accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return this.isAccountNonLocked;}public void setAccountNonLocked(boolean accountNonLocked) {this.isAccountNonLocked = accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return this.isCredentialsNonExpired;}public void setCredentialsNonExpired(boolean credentialsNonExpired) {this.isCredentialsNonExpired = credentialsNonExpired;}@Overridepublic boolean isEnabled() {return this.isEnabled;}public void setEnabled(boolean enabled) {this.isEnabled = enabled;}public long getUserId() {return this.userId;}public void setUserId(long userId) {this.userId = userId;}public List<Authority> getAuthorities2() {return this.authorities;}public String getUserName() {return this.userName;}public void setUserName(String userName) {this.userName = userName;}public String getImgURL() {return imgURL;}public void setImgURL(String imgURL) {this.imgURL = imgURL;}@Overridepublic String toString() {return "User{" +"userId=" + userId +", userName='" + userName + '\'' +", password='" + password + '\'' +", imgURL='" + imgURL + '\'' +", authorities=" + authorities +", isAccountNonExpired=" + isAccountNonExpired +", isAccountNonLocked=" + isAccountNonLocked +", isCredentialsNonExpired=" + isCredentialsNonExpired +", isEnabled=" + isEnabled +'}';}
}

Authority
权限(角色)类

package com.example.demo.entity;import org.springframework.security.core.GrantedAuthority;/*** @author Administrator*/
public class Authority implements GrantedAuthority {private long id;private String name;public Authority() {}public Authority(long id, String name) {this.id = id;this.name = name;}public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return this.name;}@Overridepublic String getAuthority() {return this.name;}
}

UserAuthorities
与用户关联的用户权限类。

package com.example.demo.entity;/*** @author Administrator*/
public class UserAuthorities {private long id;private long userId;private long authorityId;public long getId() {return id;}public void setId(long id) {this.id = id;}public long getUserId() {return userId;}public void setUserId(long userId) {this.userId = userId;}public long getAuthorityId() {return authorityId;}public void setAuthorityId(long authorityId) {this.authorityId = authorityId;}
}

ResultMsg
统一封装的后端返回消息类,里面可以弄staic字符串,更方便,这里没弄。

package com.example.demo.entity;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;import java.io.Serializable;/*** code: 0 成功, 1 失败,** @author KING*/
public class ResultMsg implements Serializable {private int code;private String msg;private JSONObject jsonObject;private JSON json;public ResultMsg() {}public ResultMsg(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public JSONObject getJsonObject() {return jsonObject;}public void setJsonObject(JSONObject jsonObject) {this.jsonObject = jsonObject;}public JSON getJson() {return json;}public void setJson(JSON json) {this.json = json;}@Overridepublic String toString() {return "ResultMsg{" +"code=" + code +", msg='" + msg + '\'' +", jsonObject=" + jsonObject +", json=" + json +'}';}
}

Page
分页类

package com.example.demo.entity;import java.io.Serializable;
import java.util.List;/*** @author KING*/
public class Page implements Serializable {private int pageIndex;private int userNumber;private int totalPage;private List<User> userList;public Page() {}public Page(int pageIndex, int userNumber, List<User> userList) {this.pageIndex = pageIndex;this.userNumber = userNumber;this.totalPage = (userNumber - 1) / 10 + 1;this.userList = userList;}public int getPageIndex() {return pageIndex;}public void setPageIndex(int pageIndex) {this.pageIndex = pageIndex;}public int getTotalPage() {return totalPage;}public void setTotalPage(int totalPage) {this.totalPage = totalPage;}public List<User> getUserList() {return userList;}public void setUserList(List<User> userList) {this.userList = userList;}public int getUserNumber() {return userNumber;}public void setUserNumber(int userNumber) {this.userNumber = userNumber;}@Overridepublic String toString() {return "Page{" +"pageIndex=" + pageIndex +", userNumber=" + userNumber +", totalPage=" + totalPage +", userList=" + userList +'}';}
}

ExcelUtils
Excel类,用于解析Excel文件,获取文件内容,需有表头格式,否则无法拿到信息,这里表头为 score、courseId、userId。

package com.example.demo.entity;import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author Administrator*/
public class ExcelUtils {/*** 总行数*/private int totalRows = 0;/*** 总条数*/private int totalCells = 0;/*** 错误信息接收器*/private String errorMsg;/*** 最多行数*/private int maxRows;public ExcelUtils() {}/*** 是否是2003的excel,返回true是2003** @param filePath* @return*/public static boolean isExcel2003(String filePath) {return filePath.matches("^.+\\.(?i)(xls)$");}/*** 是否是2007的excel,返回true是2007** @param filePath* @return*/public static boolean isExcel2007(String filePath) {return filePath.matches("^.+\\.(?i)(xlsx)$");}public int getTotalRows() {return totalRows;}public int getTotalCells() {return totalCells;}public String getErrorInfo() {return errorMsg;}public int getMaxRows() {return maxRows;}public void setMaxRows(int maxRows) {this.maxRows = maxRows;}/*** 验证EXCEL文件** @param filePath* @return*/public boolean validateExcel(String filePath) {if (filePath == null || !(isExcel2003(filePath) || isExcel2007(filePath))) {errorMsg = "文件名不是excel格式";return false;}return true;}/*** 读EXCEL文件,获取信息集合** @param* @return*/public List<Map<String, Object>> getExcelInfo(String fileName, MultipartFile multipartFile) {// 初始化信息的集合List<Map<String, Object>> mapArrayList = new ArrayList<>();// 初始化输入流InputStream inputStream = null;try {// 验证文件名是否合格if (!validateExcel(fileName)) {return null;}// 根据文件名判断文件是2003版本还是2007版本boolean isExcel2003 = true;if (isExcel2007(fileName)) {isExcel2003 = false;}// 根据新建的文件实例化输入流inputStream = multipartFile.getInputStream();// 根据excel里面的内容读取信息mapArrayList = getExcelInfo(inputStream, isExcel2003);inputStream.close();} catch (Exception e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {inputStream = null;e.printStackTrace();}}}return mapArrayList;}/*** 根据excel里面的内容读取信息** @param inputStream 输入流* @param isExcel2003 excel是2003还是2007版本* @return* @throws IOException*/public List<Map<String, Object>> getExcelInfo(InputStream inputStream, boolean isExcel2003) {List<Map<String, Object>> mapList = null;try {/*** 根据版本选择创建Workbook的方式**/Workbook workbook = null;// 当excel是2003时if (isExcel2003) {workbook = new HSSFWorkbook(inputStream);} else {// 当excel是2007时workbook = new XSSFWorkbook(inputStream);}// 读取Excel里面的信息mapList = readExcelValue(workbook);} catch (IOException e) {e.printStackTrace();}return mapList;}/*** 读取Excel里面的信息** @param workbook* @return*/public List<Map<String, Object>> readExcelValue(Workbook workbook) {List<Map<String, Object>> mapArrayList = new ArrayList<>();for (int i = 0; i < workbook.getNumberOfSheets(); i++) {// 得到一个shellSheet sheet = workbook.getSheetAt(i);// 得到Excel的行数this.totalRows = sheet.getPhysicalNumberOfRows();// 得到Excel的列数(前提是有行数)if (totalRows >= 1 && sheet.getRow(0) != null) {this.totalCells = sheet.getRow(0).getPhysicalNumberOfCells();}Map<String, Object> mapTemp;// 循环Excel行数,从第二行开始。标题不入库for (int r = 1; r < totalRows; r++) {Row row = sheet.getRow(r);if (row == null) {continue;}mapTemp = new HashMap<>();try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 循环Excel的列for (int c = 0; c < this.totalCells; c++) {Cell cell = row.getCell(c);if (null != cell) {if (c == 0) {mapTemp.put("userId", cell.getNumericCellValue());} else if (c == 1) {mapTemp.put("courseId", cell.getNumericCellValue());} else if (c == 2) {mapTemp.put("score", cell.getNumericCellValue());}}}// 添加记录mapArrayList.add(mapTemp);}}return mapArrayList;}
}

前端页面

  1. 前端页面使用thymeleaf模板引擎。
  2. 使用bootstrap,也有可视化布局工具。
  3. 使用Font Awesome图标。

页面包括

  1. login.html
  2. index.html
  3. register.html
  4. userList.html
  5. modifyPassword.html
  6. modifyPicture.html
  7. modifyUserRole.html
  8. uploadExcel.html
  9. userList.html
  10. showScore.html

模板包括

  1. footer.html
  2. head.html
  3. nav.html
  4. pageList.html
  5. resultMsg.html

模板页面,用于引用,减少重复代码

关于thymeleaf的简单使用,例子:

  • Spring官方推荐,与Spring有很好的契合度。
  • xmlns:th="http://www.thymeleaf.org"命名空间引入先。
  • th:href="@{/{role}/login/{userName}(userName=${session.user},role=${session.role})}"URL
  • th:id="container"标签属性,几乎都有thymeleaf的替代标签属性,加前缀 th:data-th-
  • th:if="${user.imgURL != null}"就是个if函数,注意,直接在${}内进行判断,其中thymeleaf方式的引用无需再加${}
  • th:class="${page.pageIndex == 2}? 'active':''"同上,一个if、else判断,多举个例子,可以用在单数行或复数行的显示上,odd?..
  • th:onclick="'javascript:doPage(\'/pageIndex/'+${page.totalPage}+'\',\'get\',null,\'userList\',null,null)'"引用JS函数,把双引号中的整条表达式都当做字符串打,其中需用转义符\',除了引用时无需如此+${page.totalPage}+
  • th:text="|共 ${page.totalPage} 页,共 ${page.userNumber} 人|"也可以使用这种写法,用||包裹着一切,文本、变量等,但出错似乎更难查一点?也许是我的错觉。
  • th:replace="~{fragments/head :: head}"引用fragments文件夹内的head文件内的名为head的模板。
  • th:fragment="resultMsg"注册为模板,名为resultMsg。
  • th:each="user : ${page.userList}"就是一个循环,变量user被userList中的user一个个赋值,在下文中使用th:text="${user.authorities}",可以有其它参数使用,其它参数一般都是用来获取${page.userList}的状态
  • <script th:inline="javascript">如此,可在本script块中使用thymeleaf语法,对于外部JS文件,可以先在页面中引入外部JS,然后在这外部JS的引入标签中使用th:inline="javascript",此时外部JS应当能够使用thymeleaf语法(估计,未测试)。
  • th:text="${#session.id}"也可以直接拿到一些数据,如session、csrf等等。
  • 详情很多,自行百度。

关于thymeleaf-extras-springsecurity5的简单使用,例子:

  • SpringSecurity与thymeleaf的配合使用,可以直接拿到SpringSecurity中的认证信息。后端对数据进行修改操作后需重新更正认证信息,不然前端显示的是老信息,甚至会引发错误。
  • xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"命名空间引入先。
  • sec:authorize="isAuthenticated()"一个判断函数,是否已认证。
  • sec:authentication="details.sessionId"与后端交互,可以拿到SpringSecurity中的认证信息。
  • sec:authentication="principal.password"同上。
  • sec:authorize="hasRole('ROLE_ADMIN')"顾名思义,只有管理员才能看到的世界。
  • 详情很多,自行百度。

footer.html
引脚模板。

<html lang="zh"xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<footer th:class="text-center" th:fragment="footer"><span>版权公告 © 1999-2020 XXX有限公司及/或其关联公司及特许人。版权所有。</span><script th:src="@{/js/jQuery.js}"></script><script th:src="@{/js/bootstrap.min.js}"></script>
</footer>
</html>

head.html
头部模板。

<html lang="zh"xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head"><title th:id="title" th:text="${title}">title</title><meta charset="UTF-8" content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"><link rel="stylesheet" th:href="@{/css/font-awesome.min.css}"><link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"><link rel="stylesheet" th:href="@{/css/bootstrap-theme.min.css}">
</head>
</html>

nav.html
导航栏模板。

<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"xmlns:th="http://www.thymeleaf.org">
<nav class="navbar navbar-default" role="navigation" th:fragment="nav"><div class="container-fluid"><div class="navbar-header"><span class="navbar-brand">XXX管理系统</span></div><div><ul class="nav navbar-nav"><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="modifyPassword">修改密码</a></li><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="modifyPicture">更换头像</a></li><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="showScore">想看看分数</a></li><li class="dropdown" sec:authorize="hasRole('ROLE_ADMIN')"><a class="dropdown-toggle" data-toggle="dropdown" href="javascript:void (0)">管理员工具 <b class="caret"></b></a><ul class="dropdown-menu"><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="registerUsers">批量注册用户</a></li><li class="divider"></li><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="usersList">用户列表</a></li><li class="divider"></li><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="modifyUserRole">修改用户身份</a></li><li class="divider"></li><li><a href="javascript:void (0)" onclick="tabPage(this)" th:id="uploadExcel">上传excel文件</a></li></ul></li></ul></div><ul class="nav navbar-nav navbar-right"><li><a th:href="@{/index}"><span class="fa fa-user"sec:authentication="principal.userName"></span>的个人中心</a></li><li><form th:action="@{/logout}" th:id="logout" th:method="post"><button style="border: none;margin-top: 14px;background-color: #fbfbfb;" type="submit"><spanclass="fa fa-sign-out"></span> 退出</button></form></li></ul></div><div style="display :none;" th:id="CSRF_token_header" th:text="${_csrf.parameterName}"></div><div style="display:none;" th:id="CSRF_token" th:text="${_csrf.token}"></div><div sec:authentication="details.sessionId" style="display:none" th:id="sessionId"></div><div sec:authentication="details.remoteAddress" style="display:none" th:id="remoteAddress"></div><div sec:authentication="principal.authorities" style="display:none" th:id="authorities"></div>
</nav>

pageList.html
分页条模板。

<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<div th:class="text-center" th:fragment="pageList"><ul class="pagination" th:if="${page.pageIndex < 3 }"><li><a href="javascript:void(0)" onclick=doPage("/pageIndex/1","get",null,"userList",null,null) th:text="首页"></a></li><li th:class="${page.pageIndex == 1}? 'active':''" th:if="${page.totalPage >= 1}"><a href="javascript:void(0)" onclick=doPage("/pageIndex/1","get",null,"userList",null,null) th:text="1"></a></li><li th:class="${page.pageIndex == 2}? 'active':''" th:if="${page.totalPage >= 2}"><a href="javascript:void(0)" onclick=doPage("/pageIndex/2","get",null,"userList",null,null) th:text="2"></a></li><li th:class="${page.pageIndex == 3}? 'active':''" th:if="${page.totalPage >= 3}"><a href="javascript:void(0)" onclick=doPage("/pageIndex/3","get",null,"userList",null,null) th:text="3"></a></li><li th:class="${page.pageIndex == 4}? 'active':''" th:if="${page.totalPage >= 4}"><a href="javascript:void(0)" onclick=doPage("/pageIndex/4","get",null,"userList",null,null) th:text="4"></a></li><li th:class="${page.pageIndex == 5}? 'active':''" th:if="${page.totalPage >= 5}"><a href="javascript:void(0)" onclick=doPage("/pageIndex/5","get",null,"userList",null,null) th:text="5"></a></li><li><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.totalPage}+'\',\'get\',null,\'userList\',null,null)'"th:text="末页"></a></li><li><span th:text="|共 ${page.totalPage} 页,共 ${page.userNumber} 人|"></span></li></ul><ul class="pagination"th:if="${page.pageIndex >= 3}"><li><a href="javascript:void(0)" onclick=doPage("/pageIndex/1","get",null,"userList",null,null) th:text="首页"></a></li><li><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.pageIndex - 2}+'\',\'get\',null,\'userList\',null,null)'"th:text="${page.pageIndex - 2}"></a></li><li><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.pageIndex -1}+'\',\'get\',null,\'userList\',null,null)'"th:text="${page.pageIndex - 1}"></a></li><li th:class="active"><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.pageIndex}+'\',\'get\',null,\'userList\',null,null)'"th:text="${page.pageIndex}"></a></li><li th:if="${page.totalPage >= (page.pageIndex + 1)}"><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.pageIndex + 1}+'\',\'get\',null,\'userList\',null,null)'"th:text="${page.pageIndex + 1}"></a></li><li th:if="${page.totalPage >= (page.pageIndex + 2)}"><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.pageIndex + 2}+'\',\'get\',null,\'userList\',null,null)'"th:text="${page.pageIndex + 2}"></a></li><li><a href="javascript:void(0)"th:onclick="'javascript:doPage(\'/pageIndex/'+${page.totalPage}+'\',\'get\',null,\'userList\',null,null)'"th:text="末页"></a></li><li><span th:text="|共 ${page.totalPage} 页,共 ${page.userNumber} 人|"></span></li></ul>
</div>
</html>

resultMsg.html
错误信息模板。

<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<div th:fragment="resultMsg" th:if="${resultMsg != null}"><div th:class="${resultMsg.getCode() == 1}? 'panel panel-danger text-center text-danger':'panel panel-success text-center text-success'"th:if="${resultMsg.getCode() == 0 || resultMsg.getCode() == 1}"><div class="panel-heading"><h3 class="panel-title" th:text="${resultMsg.getMsg()}">wrong</h3></div></div>
</div>

普通页面

login.html
登录页面。

<!DOCTYPE html>
<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<head><style type="text/css">form {border-radius: 30px;border: 2px solid #e7e7e7;background-color: #fbfbfb;}</style>
</head>
<head th:replace="~{fragments/head :: head}">
</head>
<body>
<div class="container"><div class="row col-sm-offset-3 col-sm-6 col-md-offset-3 col-md-6 col-xs-offset-3 col-xs-6 col-lg-offset-3 col-lg-6"><form th:action="@{/login}" th:method="post"><h2 th:class="text-center"><span aria-hidden="true" class="fa fa-user">系统登录</span></h2><div th:replace="~{/fragments/resultMsg ::resultMsg}"></div><div class="form-group"><label class="col-form-label" for="userId">ID</label><input class="form-control" id="userId" name="username" placeholder="请输入学号"required type="number"></div><div class="form-group"><label class="col-form-label" for="password">密码</label><input class="form-control" id="password" maxlength="16" name="password"placeholder="请输入密码" required type="password"></div><div class="form-group text-center"><button class="btn btn-default" type="submit">登录</button></div></form></div>
</div>
<footer th:replace="~{fragments/footer :: footer}">
</footer>
</body>
</html>

index.html
用户界面首页。

<!DOCTYPE html>
<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/head :: head}">
</head>
<body sec:authorize="isAuthenticated()">
<nav th:replace="~{fragments/nav :: nav}">
</nav>
<div sec:authorize="isAnonymous()"><p>sec:authorize="isAnonymous()"</p><p>是否为匿名用户函数,返回“true”/"false"</p><p>非匿名用户</p><p>应该是看不到的这一段的(security配置里也不允许无证登录)</p><p>看到了,请查看thymeleaf-extras-springsecurity版本</p><p>是否与thymeleaf相搭配</p><p>可以尝试将版本4改为5等做法</p><p>另外附thymeleaf超链接的写法:th:href="@{/{role}/login/{userName}(userName=${session.user},role=${session.role})}"</p>
</div>
<div class="container" th:id="container"><div th:replace="~{/fragments/resultMsg::resultMsg}"></div><img alt="暂无头像" class="img-responsive" height="152" style="float: left;box-shadow: 0 0 8px #999"th:if="${user.imgURL != null}"th:src="@{${user.imgURL}}"width="114"><div class="panel panel-default" style="box-shadow: 0 0 8px #999;float: left"><div class="panel-heading"><h3 class="panel-title">基本信息</h3></div><table class="table table-hover"><tbody><tr><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6">ID:</td><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6" sec:authentication="principal.userId" th:id="userId"></td></tr><tr><td>姓名:</td><td sec:authentication="principal.userName" th:id="userName"></td></tr><tr><td>密码:</td><td sec:authentication="principal.password"></td></tr><tr sec:authorize="hasRole('ROLE_ADMIN')"><td>SpringSecurity获取的sessionID:</td><td sec:authentication="details.sessionId" th:id="sessionId"></td></tr><tr sec:authorize="hasRole('ROLE_ADMIN')"><td>SpringSecurity获取的remoteAddress:</td><td sec:authentication="details.remoteAddress" th:id="remoteAddress"></td></tr><tr sec:authorize="hasRole('ROLE_ADMIN')"><td>Thymeleaf获取的sessionID:</td><td th:text="${#session.id}"></td></tr></tbody></table></div><button class="btn btn-default text-center" onclick="tabPage(this)" style="margin-left: 87%"th:id="modifyUserInformationBtn" th:text="修改信息"type="button"></button>
</div>
<footer th:replace="~{fragments/footer :: footer}">
</footer>
<script th:src="@{/js/nav.js}"></script>
</body>
</html>

register.html
注册用户。

<!DOCTYPE html>
<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"xmlns:th="http://www.thymeleaf.org">
<body sec:authorize="isAuthenticated()">
<div class="container" sec:authorize="hasRole('ROLE_ADMIN')"><div class="row col-sm-offset-3 col-sm-6 col-md-offset-3 col-md-6 col-xs-offset-3 col-xs-6 col-lg-offset-3 col-lg-6"><form><h2 th:class="text-center"><span aria-hidden="true" class="fa fa-user-plus">添加用户</span></h2><div th:replace="~{fragments/resultMsg :: resultMsg}"></div><div class="form-group"><label class="col-form-label" for="userNumber">添加数量</label><input class="form-control" id="userNumber" name="userNumber" placeholder="请输入添加数量"required type="number"></div><div class="form-group"><label class="col-form-label" for="userName">统一姓名</label><input class="form-control" id="userName" maxlength="6" name="userName"placeholder="请输入统一姓名" required></div><div class="form-group"><label class="col-form-label" for="password">统一密码</label><input class="form-control" id="password" maxlength="16" name="password"placeholder="请输入统一密码" required type="password"></div><div class="form-group"><label class="col-form-label" for="sessionID">sessionID</label><div class="form-control" id="sessionId"sec:authentication="details.sessionId"></div></div><div class="form-group"><label class="col-form-label" for="remoteAddress">remoteAddress</label><div class="form-control" id="remoteAddress"sec:authentication="details.remoteAddress"></div></div><div class="form-group"><label class="col-form-label" for="CSRF_token_header">CSRF_token_name</label><div class="form-control" id="CSRF_token_header" name="CSRF_token_header"placeholder="请输入你的csrf_name"th:text="${_csrf.parameterName}"></div></div><div class="form-group"><label class="col-form-label" for="CSRF_token">CSRF_token</label><div class="form-control" id="CSRF_token" name="CSRF_token" placeholder="请输入你的csrf_token"th:text="${_csrf.token}"></div></div><div class="form-group text-center"><button class="btn btn-default" onclick="tabPage(this)" th:id="registerSubmitBtn" type="button">提交</button></div></form></div>
</div>
</body>
</html>

modifyPassword.html
改密码。

<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"xmlns:th="http://www.thymeleaf.org">
<head><div class="row col-sm-offset-3 col-sm-6 col-md-offset-3 col-md-6 col-xs-offset-3 col-xs-6 col-lg-offset-3 col-lg-6"th:id="modifyPasswordForm"><form><h2 th:class="text-center"><span aria-hidden="true" class="fa fa-user">修改密码</span></h2><div class="panel panel-danger text-center text-danger" style="display: none" th:id="alert2"><div class="panel-heading"><h3 class="panel-title" th:id="alert">wrong</h3></div></div><div class="form-group"><label class="col-form-label" for="originPassword">请输入原密码</label><input class="form-control" id="originPassword" maxlength="16" name="originPassword"onkeyup="checkPassword()" placeholder="请输入原密码" required type="password"></div><div class="form-group"><label class="col-form-label" for="newPassword">请输入新密码</label><input class="form-control" id="newPassword" maxlength="16" minlength="6" name="newPassword"onkeyup="checkPassword()" placeholder="请输入新密码" required type="password"></div><div class="form-group"><label class="col-form-label" for="confirmPassword">请再次输入新密码</label><input class="form-control" id="confirmPassword" maxlength="16" minlength="6" name="confirmPassword"onkeyup="checkPassword()" placeholder="请再次输入新密码" required type="password"></div><div class="form-group"><label class="col-form-label" th:for="userId">userId</label><div class="form-control" sec:authentication="principal.userId"th:id="userId"></div></div><!--<div class="form-group"><label class="col-form-label" for="sessionID">sessionID</label><div class="form-control" id="sessionId"sec:authentication="details.sessionId"></div></div><div class="form-group"><label class="col-form-label" for="remoteAddress">remoteAddress</label><div class="form-control" id="remoteAddress"sec:authentication="details.remoteAddress"></div></div><div class="form-group"><label class="col-form-label" for="CSRF_token_header">CSRF_token_name</label><div class="form-control" id="CSRF_token_header" name="CSRF_token_header"th:text="${_csrf.parameterName}"></div></div><div class="form-group"><label class="col-form-label" for="CSRF_token">CSRF_token</label><div class="form-control" id="CSRF_token" name="CSRF_token"th:text="${_csrf.token}"></div></div>--><div class="form-group text-center"><button class="btn btn-default" disabled="disabled" onclick="modifyPassword()"th:id="modifyPasswordBtn" type="button">提交</button></div></form><script>var originUserId = $("#userId").text();if ($("#authorities").text().indexOf("[ROLE_ADMIN]") !== -1) {var temp = $("#userId").parent();var html = "<label class='col-form-label' th:for='userId'>userId</label>" +"<input id='userId' class='form-control' value='" + originUserId + "'>"temp.empty().html(html);}function modifyPassword() {var url = null;url = "/modifyUserPassword";var userId = null;if ($("#authorities").text().indexOf("[ROLE_ADMIN]") !== -1) {userId = $("#userId").val();} else {userId = originUserId;}var postData = {"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text(),"newPassword": $("#newPassword").val(),"originPassword": $("#originPassword").val(),"userId": userId};postData = JSON.stringify(postData);var header = $("#CSRF_token_header").text();var token = $("#CSRF_token").text();url += "?" + header + "=" + token;$.ajax({url: url,type: 'post',async: true,contentType: "application/json",data: postData,success: function (resultMsg) {alert(resultMsg.msg);if (resultMsg.code === 0 && originUserId === userId) {alert("请重新登录!")$("#logout").submit();}},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})}function checkPassword() {if ($("#originPassword").val().length < 6) {$("#alert").empty().fadeIn("slow").text("原密码至少6位!");$("#alert2").show();$("#modifyPasswordBtn").attr("disabled", true);} else if ($("#newPassword").val().length < 6) {$("#alert").empty().fadeIn("slow").text("新密码太短,至少6位!");$("#alert2").show();$("#modifyPasswordBtn").attr("disabled", true);} else if ($("#newPassword").val() !== $("#confirmPassword").val()) {$("#alert").empty().fadeIn("slow").text("两次密码还不一致哦");$("#alert2").show();$("#modifyPasswordBtn").attr("disabled", true);} else {$("#alert2").hide();$("#modifyPasswordBtn").attr("disabled", false);}}</script></div>

modifyPicture.html
传头像。

<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<div><div th:replace="~{/fragments/resultMsg::resultMsg}"></div><div class="panel panel-default" style="box-shadow: 0 0 8px #999;"><div class="panel-heading"><h3 class="panel-title">修改头像</h3></div><table class="table table-hover"><tbody><tr><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6">原头像th:style="'background-image: url('+${user.imgUrl}+')'"</td><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6"><img alt="暂无头像" class="img-responsive" height="80" style="float: left;box-shadow: 0 0 8px #999"th:if="${user.imgURL != null}"th:src="@{${user.imgURL}}"width="114"></td></tr><tr><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6">新头像<div><input accept="image/png, image/jpeg, image/jpg" style="margin-top: 2%" th:id="choicePictureBtn"type="file"><button class='btn btn-default' style="margin-top: 2%" th:id="uploadPictureBtn" type="button">上传头像</button></div></td><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6"><img alt="请选择图片以预览" class="img-responsive" height="80" style="float: left;box-shadow: 0 0 8px #999"th:id="previewPicture"width="114"></td></tr></tbody></table></div>
</div><script>var check = false;$("#choicePictureBtn").change(function () {var fileName = $("#choicePictureBtn").val();console.log(fileName);fileName = fileName.replace("C:\\fakepath\\", "");console.log(fileName);if (fileName === "") {alert("请选择图片");} else {var size = $("#choicePictureBtn")[0].files[0].size;if (size / 1024 > 100) {alert("图片大小不能超过100KB");return;}}$("#previewPicture").attr("src", URL.createObjectURL($(this)[0].files[0]));check = true;})$("#uploadPictureBtn").click(function () {if (check) {var url = "/modifyPicture?";var header = $("#CSRF_token_header").text();var token = $("#CSRF_token").text();url += header + "=" + token;var file = $("#choicePictureBtn")[0].files[0];var formData = new FormData();formData.append("modifyPicture", file);$.ajax({url: url,type: 'post',async: true,data: formData,//ajax上传图片需要添加contentType: false,processData: false,success: function (resultMsg) {alert(resultMsg.msg);window.location.reload();},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})} else {alert("图片未选择或超过100k,请重新选择图片!");}})
</script>

modifyUserRole.html
改权限。

<html lang="zh"xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<head><style>dl input, label {vertical-align: middle;}/*给选项表示设置内边距*/dl label {padding: 0 10px 0 5px;}/*给列表设置边框*/dl {width: 120px;/*元素水平居中*/margin: 10px auto;border: 1px solid #666;/*设置边框圆角*/border-radius: 5px;background: #fafafa;padding: 10px 5px;}dt {/*下边框*/border-bottom: 1px solid black;/*距离底部内边距*/padding-bottom: 10px;}/*标示字体加粗*/dt label {font-weight: 700;}/*各个选项距离顶部外边距*/dl p {margin-top: 10px;}</style><div class="row col-sm-offset-3 col-sm-6 col-md-offset-3 col-md-6 col-xs-offset-3 col-xs-6 col-lg-offset-3 col-lg-6"th:id="modifyUserRoleForm"><form><h2 th:class="text-center"><span aria-hidden="true" class="fa fa-user">修改权限</span></h2><div class="panel panel-danger text-center text-danger" style="display: none" th:id="alert2"><div class="panel-heading"><h3 class="panel-title" th:id="alert">wrong</h3></div></div><div class="form-group"><label class="col-form-label" for="userId">请输入欲修改的用户的ID</label><input class="form-control" id="userId" name="userId"onkeyup="checkUserId()" placeholder="请输入用户ID" required type="number"></div><div class="form-group"><label class="col-form-label">选择身份</label><dl class="checkBox"><dt><input id="checkAll" onclick="isCheckAll()" type="checkbox"><label>全选</label></dt><dd><p><input name="item" type="checkbox" value="1"><label>管理员</label></p><p><input checked="checked" name="item" type="checkbox" value="2"><label>用户</label></p><p><input name="item" type="checkbox" value="4"><label>教师</label></p><p><input name="item" type="checkbox" value="3"><label>临时工</label></p><p><input name="item" type="checkbox" value="5"><label>会计</label></p></dd></dl></div><div class="form-group" style="text-align:center"><button class="btn btn-primary" disabled="disabled" id="changeRoleBtn"onclick="changeRole()" type="button">提交</button></div></form><script>function checkUserId() {if ($("#userId").val() !== null && $("#userId").val() !== "") {$("#changeRoleBtn").attr("disabled", false);} else {$("#changeRoleBtn").attr("disabled", true);}}function isCheckAll() {var check = $("#checkAll").prop('checked');if (!check) {$('input[name=item]').prop('checked', false);} else {$('input[name=item]').prop('checked', true);}}function changeRole() {var url = "/modifyUserRole";var userId = $("#userId").val();var role = [];$("input[name=item]:checked").each(function () {role.push($(this).val());});var postData = {"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text(),"userId": userId,"role": role};postData = JSON.stringify(postData);alert(postData);var header = $("#CSRF_token_header").text();var token = $("#CSRF_token").text();url += "?" + header + "=" + token;$.ajax({url: url,type: 'post',async: true,contentType: "application/json",data: postData,success: function (resultMsg) {alert(resultMsg.msg);},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})}</script></div>

uploadExcel.html
上传Excel文件,表头固定为“userId,courseId,score”(顺序无关),可在ExcelUtils内修改.。

<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<style type="text/css">form {border-radius: 30px;border: 2px solid #e7e7e7;background-color: #fbfbfb;}
</style>
<div class="row col-sm-offset-3 col-sm-6 col-md-offset-3 col-md-6 col-xs-offset-3 col-xs-6 col-lg-offset-3 col-lg-6"><form enctype="multipart/form-data" th:action="@{/uploadExcel}" th:method="post"><h2 th:class="text-center"><span aria-hidden="true" class="fa fa-upload">上传excel文件</span></h2><div class="form-group"><label class="col-form-label">请输入选择文件</label><input name="excelFile" th:id="choiceUploadExcelBtn" type="file"></div><button class='btn btn-default text-center' th:id="uploadExcelBtn" type="button">提交</button></form>
</div>
<script>$("#uploadExcelBtn").click(function () {var size = $("#choiceUploadExcelBtn")[0].files[0].size;if (size === 0) {alert("请选择文件!");}var url = "/uploadExcel?";var header = $("#CSRF_token_header").text();var token = $("#CSRF_token").text();url += header + "=" + token;var file = $("#choiceUploadExcelBtn")[0].files[0];var formData = new FormData();formData.append("excelFile", file);$.ajax({url: url,type: 'post',async: true,data: formData,contentType: false,processData: false,success: function (resultMsg) {alert(resultMsg.msg);},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})})
</script>
</html>

userList.html
看用户列表。

<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/head :: head}">
</head>
<body sec:authorize="isAuthenticated()">
<div class="panel panel-default" sec:authorize="hasRole('ROLE_ADMIN')"><div class="panel-heading"><h3 class="panel-title">数据无价,谨慎操作</h3></div><div th:replace="~{fragments/resultMsg :: resultMsg}"></div><table class="table table-striped"><thead><tr><th data-field="userId">ID</th><th data-field="userName">姓名</th><th data-field="authorities">角色</th><th data-field="modify">修改</th><th data-field="delete">删除</th></tr></thead><tbody><tr th:each="user : ${page.userList}"><td th:id="userId" th:text="${user.userId}">userId</td><td th:id="userName" th:text="${user.username}">userName</td><td style="display: none" th:id="choicePassword" th:text="${user.password}">password</td><td th:text="${user.authorities}">authorities</td><td><div><!-- 按钮触发模态框 --><button class="btn btn-primary btn-lg" data-target="#myModal" data-toggle="modal"onclick="modalBtn(this)" th:id="modalBtn"><i aria-hidden="true" class="fa fa-pencil-square-o"></i></button></div></td><td><div><form><input th:id="choiceId" th:type="hidden" th:value="${user.userId}"><button class="btn btn-danger btn-lg" onclick="tabPage(this)"th:id="deleteUserSubmitBtn" type="button"><span aria-hidden="true" class="fa fa fa-times"></span></button></form></div></td></tr></tbody></table><div style="display: none" th:id="pageIndex" th:text="${page.pageIndex}"></div>
</div>
</div>
<div th:replace="~{fragments/pageList :: pageList}">
</div><!-- 模态框(Modal) -->
<div aria-hidden="true" aria-labelledby="myModalLabel" class="modal fade" id="myModal" role="dialog"tabindex="-1"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><button aria-hidden="true" class="close" data-dismiss="modal" type="button">&times;</button><h4 class="modal-title" id="myModalLabel">模态框(Modal)标题</h4></div><div class="modal-body"><table class="table table-hover"><tbody><tr><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6">ID:</td><td class="col-sm-6 col-md-6 col-xs-6  col-lg-6" th:id="modifyUserIdAdmin"></td></tr><tr><td>姓名:</td><td><input class='form-control' th:id='modifyUserNameAdmin'></td></tr></tbody></table></div><div class="modal-footer"><button class="btn btn-default" data-dismiss="modal" th:id="modalCloseBtn" type="button">关闭</button><button class="btn btn-primary" onclick="modifyInformationAdmin()"type="button">保存修改</button></div></div><!-- /.modal-content --></div><!-- /.modal -->
</div>
<script th:inline="javascript">function modalBtn(target) {var modifyUserIdAdmin = $(target).parents("tr").children().first().text();$("#modifyUserIdAdmin").text(modifyUserIdAdmin);var modifyUserNameAdmin = $(target).parents("tr").children("#userName").text();$("#modifyUserNameAdmin").val(modifyUserNameAdmin);}function modifyInformationAdmin() {var postData = {"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text(),"userName": $("#modifyUserNameAdmin").val(),"userId": $("#modifyUserIdAdmin").text()};var header = $("#CSRF_token_header").text();var token = $("#CSRF_token").text();var url = "/modifyUserInformation?" + header + "=" + token;$.ajax({url: url,type: 'post',async: true,contentType: "application/json",data: JSON.stringify(postData),success: function (resultMsg) {alert(resultMsg.msg);},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})$('#modalCloseBtn').click();var url2 = "/pageIndex/" + [[${page.pageIndex}]];var title = "userList";doPage(url2, 'get', null, title, header, token);}
</script>
</body>
</html>

showScore.html
看分数。

<!DOCTYPE html>
<html lang="zh" xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org">
<div><div class="panel panel-default" style="box-shadow: 0 0 8px #999;"><div class="panel-heading"><h3 class="panel-title">基本信息</h3></div><table class="table table-hover"><thead><tr><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2" data-field="userId">学号</th><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2" data-field="userName">姓名</th><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2" data-field="courseName">课程名</th><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2" data-field="score">分数</th><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2" data-field="time">开课时间</th><th class="col-sm-2 col-md-2 col-xs-2  col-lg-2">评价</th></tr></thead><tbody class="myTbody"><tr th:each="score : ${scoreList}"><td th:text="${score.userId}">1</td><td th:text="${score.userName}">1</td><td th:text="${score.courseName}">profession</td><td th:id="courseScore" th:text="${score.score}">authorities</td><td th:text="${score.time}">authorities</td><td th:id="appraisal">appraisal</td></tr></tbody></table></div>
</div>
<script>$("td[id=courseScore]").each(function () {if ($(this).text() < 60) {$(this).parent().children("#appraisal").text("不及格");} else if ($(this).text() <= 80) {$(this).parent().children("#appraisal").text("一般");} else if ($(this).text() <= 90) {$(this).parent().children("#appraisal").text("良好");} else {$(this).parent().children("#appraisal").text("优秀");}})
</script>
</html>

JavaScript

有一定数量的页面都需用到的JS函数就放在一个JS文件里,直接引用,否则写在页面内,减少不必要的流量,节省带宽。
nav.js

function tabPage(tag) {var id = tag.id;var url = null;var check = "get";var postData = null;var title = null;var header = null;var token = null;var html = null;var originURL = window.location.href;console.log(id);switch (id) {case "registerUsers": {url = "/register";title = "register";break;}case "registerSubmitBtn": {var r = confirm("确认信息并注册?!");if (r) {url = "/register";check = "post";title = "userList";postData = {"userNumber": $("#userNumber").val(),"userName": $("#userName").val(),"password": $("#password").val(),"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text()};postData = JSON.stringify(postData);}break;}case "deleteUserSubmitBtn": {var r = confirm("确认删除?!");if (r) {url = "/pageIndex/deleteUser"check = "post";title = "userList";postData = {"userId": $(tag).parents('form').children().first().val(),"pageIndex": $("#pageIndex").text(),"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text()};postData = JSON.stringify(postData);}break;}case "usersList": {url = "/pageIndex/1";title = "userList";break;}case "modifyPassword": {url = "/modifyUserPassword";title = "modifyPassword";break;}case "modifyPicture": {url = "/modifyPicture";title = "modifyPicture";break;}case "modifyUserRole": {url = "/modifyUserRole";title = "modifyUserRole";break;}case "uploadExcel": {url = "/uploadExcel";title = "uploadExcel";break;}case "showScore": {url = "/showScore";title = "showScore";break;}case "modifyUserInformationBtn": {if ($("#modifyUserInformationBtn").text() === "修改信息") {html = "<input id='modifyUserName' class='form-control' value='" + $("#userName").text() + "'>"$("#userName").empty().html(html);$("#modifyUserInformationBtn").text("保存修改");break;} else {postData = {"sessionId": $("#sessionId").text(),"remoteAddress": $("#remoteAddress").text(),"userName": $("#modifyUserName").val()};postData = JSON.stringify(postData);title = "index";check = 'post';header = $("#CSRF_token_header").text();token = $("#CSRF_token").text();/*由于SpringSecurity有csrf防护,所以给URL添加CSRF_TOKEN*/url = "/modifyUserInformation?" + header + "=" + token;$.ajax({url: url,type: check,async: true,contentType: "application/json",data: postData,success: function (resultMsg) {alert(resultMsg.msg);window.location.reload();},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})return;break;}}default: {return;}}doPage(url, check, postData, title, header, token);
}function doPage(url, check, postData, title, header, token) {if (url == null || check == null) {return;}var log = {"url": url,"check": check,"postData": postData,"title": title,"header": header,"token": token,};log = JSON.stringify(log);console.log(log);if (check == "get") {$.ajax({url: url,type: check,async: true,success: function (data) {$("#title").text(title);$("#container").empty().html(data);},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})} else {header = $("#CSRF_token_header").text();token = $("#CSRF_token").text();/*由于SpringSecurity有csrf防护,所以给URL添加CSRF_TOKEN*/url += "?" + header + "=" + token;$.ajax({url: url,type: check,async: true,contentType: "application/json",data: postData,success: function (data) {console.log("success get return");$("#title").text(title);$("#container").empty().html(data);},error: function (XMLHttpRequest, textStatus, errorThrown) {alert(XMLHttpRequest.status);alert(XMLHttpRequest.readyState);alert(textStatus);},})}
}

控制层

与前端交互,给服务层传递请求。

MainController.java

package com.example.demo.controller;import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.ExcelUtils;
import com.example.demo.entity.Page;
import com.example.demo.entity.ResultMsg;
import com.example.demo.entity.User;
import com.example.demo.service.MainServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;/*** @author Administrator*/
@RestController
public class MainController {@AutowiredMainServiceImpl mainServiceImpl;@GetMapping(value = {"/", "/login"})public ModelAndView getLogin() {System.out.println("getLogin start!");/*用于最初无用户时批量加入用户User user = new User("test","123456");this.mainServiceImpl.addUser(100,user);*///用于更改密码,搞砸用/* BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String password = bCryptPasswordEncoder.encode("123456");JSONObject jsonObject = new JSONObject();jsonObject.put("userId",1);jsonObject.put("newPassword",password);this.mainServiceImpl.modifyUserPassword(jsonObject);*/return new ModelAndView("/login", "title", "login");}/*** 登录成功重定向跳转到 " /index"** @return ModelAndView*/@PostMapping(value = {"/loginSuccess"})public ModelAndView postLogin() {System.out.println("postLogin start!");return new ModelAndView("redirect:/index");}@GetMapping(value = {"/index"})public ModelAndView getIndex(Model model) {System.out.println("getIndex start!");System.out.println("以下为用户信息!");System.out.println(SecurityContextHolder.getContext().getAuthentication().getDetails());User user = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal());model.addAttribute("user", user);return new ModelAndView("/index", "title", "index");}@GetMapping(value = {"/login-error"})public ModelAndView loginError(Model model) {System.out.println("login-error start!");ResultMsg resultMsg = new ResultMsg(1, "啊哦,ID或密码错误,请重试!");model.addAttribute("title", "login-error");return new ModelAndView("/login", "resultMsg", resultMsg);}@GetMapping(value = {"/register"})public ModelAndView getRegister() {System.out.println("get register start!");return new ModelAndView("/register", "title", "register");}/*** 注册用户** @param jsonObject* @param model* @return ModelAndView*/@PostMapping(value = {"/register"})public ModelAndView postRegister(@RequestBody JSONObject jsonObject, Model model) {System.out.println("post register start!");String userName = "userName";String password = "password";if (checkUserAdmin() && checkUser(jsonObject)) {User user = new User(jsonObject.getString(userName), jsonObject.getString(password));try {this.mainServiceImpl.addUser(jsonObject.getInteger("userNumber"), user);ResultMsg resultMsg = new ResultMsg(0, "注册成功啦!请查看!");model.addAttribute("resultMsg", resultMsg);return pageIndex((this.mainServiceImpl.getUsersNumber() - 1) / 10 + 1, model);} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "注册发生错误,已中止该操作!");return new ModelAndView("/register", "resultMsg", resultMsg);}} else {ResultMsg resultMsg = new ResultMsg(1, "检测到sessionId及RemoteAddress不合法!已中止该操作!");return new ModelAndView("/register", "resultMsg", resultMsg);}}@GetMapping(value = {"/pageIndex/{pageIndex}"})public ModelAndView pageIndex(@PathVariable("pageIndex") int pageIndex, Model model) {System.out.println("get pageIndex " + pageIndex + " start!");Page page = this.mainServiceImpl.getUserList(pageIndex);String resultMsg = "resultMsg";if (!model.containsAttribute(resultMsg)) {model.addAttribute(resultMsg, null);}return new ModelAndView("/userList", "page", page);}/*** 删除用户** @param jsonObject* @param model* @return ModelAndView*/@PostMapping(value = {"/pageIndex/deleteUser"})public ModelAndView deleteUser(@RequestBody JSONObject jsonObject, Model model) {System.out.println("post deleteUser start!");if (checkUserAdmin() && checkUser(jsonObject)) {try {String pageIndex = "pageIndex";String userId = "userId";this.mainServiceImpl.deleteUser(jsonObject.getInteger(userId));ResultMsg resultMsg = new ResultMsg(0, "删除用户操作成功!");model.addAttribute("title", "userList");model.addAttribute("resultMsg", resultMsg);System.out.println("post deleteUser " + jsonObject.getInteger(userId) + " success!");return pageIndex(jsonObject.getInteger(pageIndex), model);} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "删除用户操作发生错误!删除用户操作中止!");model.addAttribute("title", "userList");model.addAttribute("resultMsg", resultMsg);return pageIndex((this.mainServiceImpl.getUsersNumber() - 1) / 10 + 1, model);}} else {ResultMsg resultMsg = new ResultMsg(1, "检测到sessionId及RemoteAddress不合法!删除用户操作中止!");model.addAttribute("title", "userList");model.addAttribute("resultMsg", resultMsg);return pageIndex((this.mainServiceImpl.getUsersNumber() - 1) / 10 + 1, model);}}/*** 修改用户信息** @param jsonObject* @return ModelAndView*/@PostMapping(value = {"/modifyUserInformation"})public ResultMsg modifyUserInformation(@RequestBody JSONObject jsonObject) {System.out.println("post modifyUserInformation start!");boolean checkAdmin = false;/*检测参数携带的sessionId与remoteAddress*/if (checkUser(jsonObject)) {/*检查是否为用户自行更改*//*是,参数则无userId,为参数增加userId字段,值为自身userId*//*不是,参数有userId,此时增加检测用户是否为管理员*/if (!jsonObject.containsKey("userId")) {User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();long userId = user.getUserId();jsonObject.put("userId", userId);} else {if (!checkUserAdmin()) {ResultMsg resultMsg = new ResultMsg(1, "非管理员禁止修改他人信息!");return resultMsg;}checkAdmin = true;}try {this.mainServiceImpl.modifyUserInformation(jsonObject);} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "修改信息操作发生错误!修改信息操作中止!");return resultMsg;}if (!checkAdmin) {String userName = "userName";((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).setUserName(jsonObject.getString(userName));}ResultMsg resultMsg = new ResultMsg(0, "信息修改成功!");return resultMsg;} else {ResultMsg resultMsg = new ResultMsg(1, "检测到sessionId及RemoteAddress不合法!修改信息操作中止!");return resultMsg;}}/*** 修改用户密码** @return ModelAndView*/@GetMapping(value = {"/modifyUserPassword"})public ModelAndView modifyUserPassword() {System.out.println("get modifyUserPassword start!");return new ModelAndView("/modifyPassword");}/*** 修改用户密码** @param jsonObject* @return ModelAndView*/@PostMapping(value = {"/modifyUserPassword"})public ResultMsg modifyUserPassword(@RequestBody JSONObject jsonObject) {System.out.println("post modifyUserPassword start!");if (checkUser(jsonObject)) {User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String originPassword = "originPassword";if (!bCryptPasswordEncoder.matches(jsonObject.getString(originPassword), user.getPassword())) {ResultMsg resultMsg = new ResultMsg(1, "原密码不正确!");return resultMsg;}long userId = user.getUserId();String newPassword = jsonObject.getString("newPassword");jsonObject.fluentRemove("newPassword");newPassword = bCryptPasswordEncoder.encode(newPassword);jsonObject.put("newPassword", newPassword);System.out.println(jsonObject.get("userId"));System.out.println(jsonObject.getString("userId"));System.out.println(jsonObject.getLongValue("userId"));System.out.println(jsonObject.getLong("userId"));if (jsonObject.getLong("userId") != userId) {if (!checkUserAdmin()) {ResultMsg resultMsg = new ResultMsg(1, "非管理员禁止修改其它用户密码!");return resultMsg;}}try {this.mainServiceImpl.modifyUserPassword(jsonObject);} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "修改密码操作发生错误!修改密码操作中止!");return resultMsg;}ResultMsg resultMsg = new ResultMsg(0, "修改密码操作成功!");return resultMsg;} else {ResultMsg resultMsg = new ResultMsg(1, "检测到sessionId及RemoteAddress不合法!密码修改操作中止!");return resultMsg;}}/*** 修改头像 get** @return ModelAndView*/@GetMapping(value = {"/modifyPicture"})public ModelAndView modifyPicture() {System.out.println("modifyPicture get start!");User user = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal());return new ModelAndView("/modifyPicture", "user", user);}/*** 修改头像 post** @return ModelAndView*/@PostMapping(value = {"/modifyPicture"})public ResultMsg modifyPicture(MultipartFile modifyPicture) {System.out.println("modifyPicture post start!");/*空文件、空文件名检查*/if (modifyPicture.isEmpty() || modifyPicture.getOriginalFilename() == null || modifyPicture.getSize() == 0 || modifyPicture.getOriginalFilename() == "") {ResultMsg resultMsg = new ResultMsg(1, "文件为空或无文件名!");return resultMsg;}long userId = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserId();/*创建目录*/String dirPath = "D:/uploadPicture/" + userId + "/";File directory = new File(dirPath);if (!directory.exists()) {directory.mkdirs();}/*文件名加入时间戳*/String fileName = modifyPicture.getOriginalFilename();String suffixName = fileName.substring(fileName.lastIndexOf(".") + 1);String fileTime = new SimpleDateFormat("yyyyMMdd-HH-mm-ss").format(new Date());/*储存图片*/try {byte[] bytes = modifyPicture.getBytes();Path path = Paths.get(dirPath + fileTime + "." + suffixName);System.out.println(path.toString());Files.write(path, bytes);String imgURL = "/image/" + userId + "/" + fileTime + "." + suffixName;this.mainServiceImpl.modifyPicture(imgURL, userId);((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).setImgURL(imgURL);ResultMsg resultMsg = new ResultMsg(0, "头像上传成功啦!");return resultMsg;} catch (IOException e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "头像上传过程出现错误!已中止操作!");return resultMsg;}}/*** 修改用户权限  get** @return ModelAndView*/@GetMapping(value = {"/modifyUserRole"})public ModelAndView modifyUserRole() {System.out.println("modifyUserRole get start!");return new ModelAndView("/modifyUserRole");}/*** 修改用户权限 post** @param jsonObject* @return ResultMsg*/@PostMapping(value = {"/modifyUserRole"})public ResultMsg modifyUserRole(@RequestBody JSONObject jsonObject) {System.out.println("modifyUserRole post start!");System.out.println(jsonObject.toString());ResultMsg resultMsg = new ResultMsg(0, "收到!");if (!checkUser(jsonObject)) {resultMsg.setMsg("检测到sessionId及RemoteAddress不合法!修改用户权限操作中止!");resultMsg.setCode(1);return resultMsg;} else if (!checkUserAdmin()) {resultMsg.setMsg("非管理员禁止修改用户权限!");resultMsg.setCode(1);return resultMsg;}if (jsonObject.getJSONArray("role") == null || jsonObject.getJSONArray("role").size() == 0) {resultMsg.setCode(1);resultMsg.setMsg("权限不可为空!");} else {try {resultMsg = this.mainServiceImpl.modifyUserRole(jsonObject);} catch (Exception e) {e.printStackTrace();resultMsg.setMsg("修改权限操作发生错误!操作中止!");resultMsg.setCode(1);return resultMsg;}}return resultMsg;}/*** 上传Excel文件** @return ModelAndView*/@GetMapping(value = {"/uploadExcel"})public ModelAndView uploadExcel() {System.out.println("uploadExcel get start!");return new ModelAndView("/uploadExcel");}/*** 上传Excel文件** @param excelFile* @return ResultMsg*/@PostMapping("/uploadExcel")public ResultMsg uploadExcel(@RequestParam("excelFile") MultipartFile excelFile) {System.out.println("uploadExcel post start!");ResultMsg resultMsg = new ResultMsg(0, "上传成功啦!");// 获取文件名String name = excelFile.getOriginalFilename();long size = excelFile.getSize();// 判断文件是否为空,其大小是否为0或其名称是否为nullif (excelFile == null || name == null || ("").equals(name) && size == 0) {resultMsg.setCode(1);resultMsg.setMsg("文件为空!");return resultMsg;}ExcelUtils readExcel = new ExcelUtils();// 解析excel,获取信息集合。List<Map<String, Object>> excelInfo = readExcel.getExcelInfo(name, excelFile);if (excelInfo == null || excelInfo.isEmpty()) {resultMsg.setCode(1);resultMsg.setMsg("文件非Excel格式文件!请重新检查上传!");return resultMsg;}try {this.mainServiceImpl.insertExcel(excelInfo);} catch (Exception e) {e.printStackTrace();resultMsg.setMsg("上传excel文件操作发生错误!操作中止!");resultMsg.setCode(1);return resultMsg;}return resultMsg;}@GetMapping(value = {"/showScore"})public ModelAndView showScore() {long userId = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserId();List<Map<String, String>> map = this.mainServiceImpl.getScore(userId);System.out.println(map.toString());return new ModelAndView("/showScore", "scoreList", map);}/*** 检测用户有无管理员权限** @return boolean*/public boolean checkUserAdmin() {System.out.println("checkUserAdmin start!");String role_Admin = "[ROLE_ADMIN]";User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (user.getAuthorities().isEmpty()) {System.out.println("user.getAuthorities().isEmpty(): " + user.getAuthorities().isEmpty());return false;}if (user.getAuthorities2().toString().contains(role_Admin)) {System.out.println("user.getAuthorities().contains(role_Admin) is success");return true;}System.out.println("checkUserAdmin is failed");return false;}/*** 检测用户有是否有风险** @param jsonObject* @return boolean*/public boolean checkUser(JSONObject jsonObject) {System.out.println("checkUser start!");String sessionId = "sessionId";String remoteAddress = "remoteAddress";WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();if (Objects.equals(webAuthenticationDetails.getSessionId(), jsonObject.getString(sessionId))) {if (Objects.equals(webAuthenticationDetails.getRemoteAddress(), jsonObject.getString(remoteAddress))) {System.out.println("checkUser is success");return true;}}System.out.println("checkUser is failed");return false;}
}

服务层

接受控制层请求,调用数据访问层函数。
MainServiceImpl

package com.example.demo.service;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.dao.AuthorityDao;
import com.example.demo.dao.ScoreDao;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.Page;
import com.example.demo.entity.ResultMsg;
import com.example.demo.entity.User;
import com.example.demo.entity.UserAuthorities;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;
import java.util.Map;/*** @author Administrator*/
@Service
public class MainServiceImpl implements UserDetailsService {@Autowiredprivate UserDao userDao;@Autowiredprivate AuthorityDao authorityDao;@Autowiredprivate ScoreDao scoreDao;/*** SpringSecurity 用户认证,返回的user包含了所有该user信息** @param userId* @return User*/@Overridepublic User loadUserByUsername(String userId) throws UsernameNotFoundException {System.out.println("userId = " + userId);try {System.out.println("try find user!");User user = this.userDao.findUser(Long.parseLong(userId));if (user != null) {System.out.println("find user success!");return user;} else {throw new UsernameNotFoundException("用户名或者密码错误");}} catch (Exception e) {e.printStackTrace();return null;}}/*** 批量录入用户** @param num* @param user*/@Transactional(rollbackFor = Exception.class)public void addUser(int num, User user) {try {for (int i = 0; i < num; i++) {this.userDao.insertUsers(user);/*默认为普通用户*/UserAuthorities ua = new UserAuthorities();ua.setUserId(user.getUserId());ua.setAuthorityId(2);this.authorityDao.insertUserAuthority(ua);}} catch (Exception e) {System.out.println("register users failed!error information: " + e.getMessage());e.printStackTrace();return;}}/*** service层删除用户** @param userId* @return void*/@Transactional(rollbackFor = Exception.class)public void deleteUser(int userId) {this.userDao.deleteUser(userId);return;}/*** 分页用户列表** @param pageIndex* @return List<User>*/public Page getUserList(int pageIndex) {Page page = new Page(pageIndex, getUsersNumber(), this.userDao.getUserList(10 * (pageIndex - 1)));return page;}/*** 获取所有用户个数** @return int*/public int getUsersNumber() {return this.userDao.getUsersNumber();}/*** 修改用户信息** @return void*/@Transactional(rollbackFor = Exception.class)public void modifyUserInformation(JSONObject jsonObject) {this.userDao.modifyUserInformation(jsonObject);return;}/*** 修改用户密码** @return int*/@Transactional(rollbackFor = Exception.class)public void modifyUserPassword(JSONObject jsonObject) {this.userDao.modifyUserPassword(jsonObject);return;}/*** 修改用户头像地址** @return int*/@Transactional(rollbackFor = Exception.class)public void modifyPicture(String imgURL, long userId) {this.userDao.modifyPicture(imgURL, userId);return;}/*** 修改用户权限** @param jsonObject* @return void*/@Transactional(rollbackFor = Exception.class)public ResultMsg modifyUserRole(JSONObject jsonObject) {try {this.authorityDao.deleteUserAuthority(jsonObject.getLong("userId"));} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "修改权限操作发生错误!操作中止!");return resultMsg;}JSONArray role = jsonObject.getJSONArray("role");for (int i = 0; i < role.size(); i++) {UserAuthorities ua = new UserAuthorities();ua.setUserId(jsonObject.getLong("userId"));ua.setAuthorityId(role.getLong(i));try {this.authorityDao.insertUserAuthority(ua);} catch (Exception e) {e.printStackTrace();ResultMsg resultMsg = new ResultMsg(1, "修改权限操作发生错误!操作中止!");return resultMsg;}}ResultMsg resultMsg = new ResultMsg(0, "修改权限操作成功!");return resultMsg;}/*** 导入excel** @param mapList* @return void*/@Transactional(rollbackFor = Exception.class)public void insertExcel(List<Map<String, Object>> mapList) {for (int i = 0; i < mapList.size(); i++) {try {this.scoreDao.insertScore(mapList.get(i));} catch (Exception e) {e.printStackTrace();}}}public List<Map<String, String>> getScore(long userId) {return this.scoreDao.getUserScoreByUserId(userId);}
}

数据访问层(Dao,Data Access Object)

从数据库拿数据。

  1. UserDao.java
  2. AuthorityDao.java
  3. ScoreDao.java
    UserDao.java
package com.example.demo.dao;import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import org.springframework.stereotype.Repository;import java.util.List;/*** @author Administrator*/
@Mapper
@Repository
public interface UserDao {/*** 通过userId查找此人** @param userId* @return User*/@Select("select * from User where UserId = #{userId}")@Results({@Result(column = "userId", property = "userId"),@Result(column = "userName", property = "userName"),@Result(column = "password", property = "password"),@Result(column = "userId", property = "authorities",many = @Many(select = "com.example.demo.dao.AuthorityDao.findUserAuthoritiesByUserId", fetchType = FetchType.LAZY))})User findUser(long userId);/*** 增加用户** @param user* @return void*/@Insert("insert into User(UserName,Password) values(#{userName},#{password})")@Options(useGeneratedKeys = true, keyProperty = "userId", keyColumn = "userId")void insertUsers(User user);/*** 删除用户** @param userId* @return void*/@Delete("delete from User where userId = #{userId}")void deleteUser(int userId);/*** 获取所有用户** @param pageIndex* @return List<User>*/@Select("select * from User limit 10 offset #{pageIndex}")@Results({@Result(column = "userId", property = "userId"),@Result(column = "userName", property = "userName"),@Result(column = "password", property = "password"),@Result(column = "userId", property = "authorities",many = @Many(select = "com.example.demo.dao.AuthorityDao.findUserAuthoritiesByUserId", fetchType = FetchType.LAZY))})List<User> getUserList(int pageIndex);/*** 获取所有用户数量** @return int*/@Select("select count(*) from User")int getUsersNumber();/*** 修改用户信息** @param jsonObject* @return void*/@Update("update User set UserName = #{userName} where UserId = #{userId}")void modifyUserInformation(JSONObject jsonObject);/*** 修改用户密码** @param jsonObject* @return void*/@Update("update User set Password = #{newPassword} where UserId = #{userId}")void modifyUserPassword(JSONObject jsonObject);/*** 修改用户头像地址** @param imgURL* @param userId* @return void*/@Update("update User set ImgURL = #{imgURL} where UserId = #{userId}")void modifyPicture(String imgURL, long userId);
}

AuthorityDao.java

package com.example.demo.dao;import com.example.demo.entity.Authority;
import com.example.demo.entity.UserAuthorities;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;import java.util.List;/*** @author Administrator*/
@Mapper
@Repository
public interface AuthorityDao {/*** 查询user的权限** @param userId* @return List<Authority>*/@Select("select * from Authority where Id in (select AuthorityId from UserAuthority where UserId=#{userId})")List<Authority> findUserAuthoritiesByUserId(long userId);/*** 插入user权限** @param ua* @return void*/@Insert("insert into UserAuthority(UserId,AuthorityId) values(#{userId},#{authorityId})")@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")void insertUserAuthority(UserAuthorities ua);/*** 删除user权限** @param userId* @return void*/@Delete("delete from UserAuthority where UserId = #{userId}")void deleteUserAuthority(long userId);
}

ScoreDao.java

package com.example.demo.dao;import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;import java.util.List;
import java.util.Map;/*** @author Administrator*/
@Mapper
@Repository
public interface ScoreDao {/*** 查询用户的分数** @param userId* @return List<Authority>*/@Select("SELECT s.userId, s.userName , cs.courseName , sc.score ,cs.time \n" +"\n" +"FROM user s, score sc,course cs\n" +"\n" +"WHERE s.userId=sc.userId AND cs.courseId = sc.courseId AND s.userId=#{userId} ")@Results({@Result(column = "userId", property = "userId")})List<Map<String, String>> getUserScoreByUserId(long userId);/*** 导入分数** @param map* @return*/@Insert("insert into Score(UserId,CourseId,Score) values(#{userId},#{courseId},#{score})")void insertScore(Map<String, Object> map);}

ScoreDao.java

package com.example.demo.dao;import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;import java.util.List;
import java.util.Map;/*** @author Administrator*/
@Mapper
@Repository
public interface ScoreDao {/*** 查询用户的分数** @param userId* @return List<Authority>*/@Select("SELECT s.userId, s.userName , cs.courseName , sc.score ,cs.time \n" +"\n" +"FROM user s, score sc,course cs\n" +"\n" +"WHERE s.userId=sc.userId AND cs.courseId = sc.courseId AND s.userId=#{userId} ")@Results({@Result(column = "userId", property = "userId")})List<Map<String, String>> getUserScoreByUserId(long userId);/*** 导入分数** @param map* @return*/@Insert("insert into Score(UserId,CourseId,Score) values(#{userId},#{courseId},#{score})")void insertScore(Map<String, Object> map);}

SpringSecurity 配置

Spring官方安全设计,可以简单看为一堆过滤器的集合,抓住请求后一个一个刷过滤器。本系统开启csrf防护,使用thymeleaf语法进行post可以省略在URL中自行加上csrf的name与token的步骤,本系统更多是自行加上。
SpringSecurityConfig.java

package com.example.demo.springsecurityconfig;import com.example.demo.service.MainServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author Administrator*/
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MainServiceImpl mainServiceImpl;/*** SpringSecurity自定义配置**/@Overrideprotected void configure(HttpSecurity http) throws Exception {http/*开启授权请求设置*/.authorizeRequests()/*这些网址任何人均可访问*/.antMatchers("/css/**", "/js/**", "/fonts/**", "/login").permitAll()/*这些网址需[ROLE_ADMIN]角色方可访问,配置中角色前缀“ROLE_”无需写入*/.antMatchers("/register", "/userList").hasRole("ADMIN")/*基于form表单登录验证*/.and().formLogin()/*自定义登录界面、登录失败页面、成功登录跳转页面(post方式)*/.loginPage("/login").failureUrl("/login-error").successForwardUrl("/loginSuccess")/*自定义登出界面,登出成功跳转页面,且清除身份认证信息,使session无效(默认post)*/.and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID")/*自定义异常页面*/.and().exceptionHandling().accessDeniedPage("/403")/*自定义安全连接页面,使用“https”.and().requiresChannel().antMatchers("/login").requiresSecure()*//*session管理,失效跳转登录页面,仅允许单用户登录一个账号,且登录中的账号不可被踢出*/.and().sessionManagement().invalidSessionUrl("/login").maximumSessions(1).maxSessionsPreventsLogin(true).expiredUrl("/login");//解决中文乱码问题CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");filter.setForceEncoding(true);http.addFilterBefore(filter, CsrfFilter.class);}/*** 认证信息管理* 设置userDetailsService位置,即实现它的类* 设置所有密码加密方式为BCrypt,如此前端传值过来会自动转化** @param auth**/@Autowired@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(mainServiceImpl).passwordEncoder(new BCryptPasswordEncoder());}/*** 资源映射路径,此处用于前端回显图片,隐藏图片真实地址*/@Configurationpublic class MyWebAppConfigurer implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/image/**").addResourceLocations("file:D:/uploadPicture/");}}
}

跑系统






制作基于springboot的简易学生管理系统(详细)相关推荐

  1. 基于SpringBoot+Vue的学生成绩管理系统

    基于SpringBoot+Vue的学生成绩管理系统 ,已经实现了增删改查,完美的springBoot项目脚手架 ,适合学习和二次开发,课程设计 已经实现了增删改查和搜索查询所有功能,完美的spring ...

  2. 基于 SpringBoot + Vue 的学生公寓管理系统

    学生公寓管理系统 简介 基于 SpringBoot + Vue 的学生公寓管理系统,自定义了权限拦截器进行权限认证与授权,使用 aop+log4j 进行日志记录,使用 reids 作为缓存,使用 my ...

  3. 基于springboot的的学生干部管理系统

    <基于springboot的的学生干部管理系统>该项目含有源码.论文等资料.配套开发软件.软件安装教程.项目发布教程等.系统功能完整,适合作为毕业设计.课程设计.数据库大作业学习使用. 项 ...

  4. java课件:基于springboot智慧校园后勤管理系统(springboot+vue+mysql+redis) 1017

    项目描述 基于springboot智慧校园后勤管理系统, 包括管理员端,业务员端. 管理员主要功能:用户管理,角色管理,学生管理:职工管理:案件管理 等; 业务员主要功能:学生管理:职工管理:案件管理 ...

  5. java-net-php-python-springboot基于SpringBoot的OA办公管理系统计算机毕业设计程序

    java-net-php-python-springboot基于SpringBoot的OA办公管理系统计算机毕业设计程序 java-net-php-python-springboot基于SpringB ...

  6. 计算机毕业设计springboot基于springboot的母婴服务管理系统qyh5j源码+系统+程序+lw文档+部署

    计算机毕业设计springboot基于springboot的母婴服务管理系统qyh5j源码+系统+程序+lw文档+部署 计算机毕业设计springboot基于springboot的母婴服务管理系统qy ...

  7. 基于SPRINGBOOT的校企合作管理系统

    开发工具(eclipse/idea): eclipse4.5/4.8或者idea2018,jdk1.8 数据库:mysql 功能模块: 基于springboot的校企合作管理系统,将用户分为三种权限: ...

  8. 基于SpringBoot的体育场馆运营管理系统的设计与实现

    [摘要] 随着国家经济的告诉发展,人均生活水平在逐步提高,计算机网络技术的发展又在改变着人们生活.工作的方式.近一两年迎来了一股全民健身的热潮,像之前抖音直播间刘耕宏的健身操,带动了不少人的模仿.同时 ...

  9. 基于SpringBoot的城市建设用地管理系统的设计与实现

    作者主页:Designer 小郑 作者简介:Java全栈软件工程师一枚,来自浙江宁波,负责开发管理公司OA项目,专注软件前后端开发(Vue.SpringBoot和微信小程序).系统定制.远程技术指导. ...

最新文章

  1. Python服务器开发一:python基础
  2. linux逻辑分区被删除了怎么办,找到了linux分区顺序错乱修复方法
  3. e会学中C语言课程考试答案,管理信息系统期末考试A试卷答案卷
  4. (转载)C#提取汉字拼音首字母的方法
  5. 新版开发工具?全新智能设备?华为开发者大会2021(Together)来了!
  6. AWK 高端大气上档次
  7. 博途PLC和ABB变频器PN通讯详解
  8. 基于STM32的高精度温度测控系统-PCB设计
  9. 安全狗又拿下一场重保胜战 第22届投洽会顺利谢幕
  10. 笔记:常见的约束问题求解算法——乘子法和Frank-Wolfe算法
  11. 利用Google Drive將英文版的PDF翻译成中文版的PDF
  12. Blender建模汇总
  13. 计算机之父图灵获英女王赦免
  14. 火车票能不能选座_终于,买火车票也能选座了!
  15. HBase的安装和使用
  16. 软考·系统架构师论文——论软件的高并发设计
  17. 业务异步写mysql数据库_把重要的业务日志异步批量写入数据库
  18. adobe Adobe Acrobat DC资源下载
  19. 易车上汽车报价准不准
  20. 数学知识—不同数据范围求组合数,例题、思路、代码实现

热门文章

  1. 营养食谱小贴士:选择食物巧防职业病
  2. excel文件打开密码如何找回
  3. Web前端day03-CSSC选择器
  4. SEO搜索引擎优化的来龙去脉
  5. Backtrack5 R1 中文支持 ibus输入法
  6. 注意力机制详解系列(三):空间注意力机制
  7. 如何用python选股票_用Python选一个自己的股票池1
  8. 广州计算机中心杜云飞,走进国家超级计算广州中心 看天河二号如何运转
  9. 蓝牙mesh设备接入天猫精灵或者百度音响
  10. ubc 文学院 计算机,英属哥伦比亚大学UBC专业