制作基于springboot的简易学生管理系统(详细)
制作基于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
项目结构
创建实体类
- User.java
- Authority.java
- UserAuthorities.java
- ResultMsg.java
- Page.java
- 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;}
}
前端页面
- 前端页面使用thymeleaf模板引擎。
- 使用bootstrap,也有可视化布局工具。
- 使用Font Awesome图标。
页面包括
- login.html
- index.html
- register.html
- userList.html
- modifyPassword.html
- modifyPicture.html
- modifyUserRole.html
- uploadExcel.html
- userList.html
- showScore.html
模板包括
- footer.html
- head.html
- nav.html
- pageList.html
- resultMsg.html
模板页面,用于引用,减少重复代码
关于thymeleaf的简单使用,例子:
- Spring官方推荐,与Spring有很好的契合度。
xmlns:th="http://www.thymeleaf.org"
命名空间引入先。th:href="@{/{role}/login/{userName}(userName=${session.user},role=${session.role})}"
URLth: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">×</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)
从数据库拿数据。
- UserDao.java
- AuthorityDao.java
- 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的简易学生管理系统(详细)相关推荐
- 基于SpringBoot+Vue的学生成绩管理系统
基于SpringBoot+Vue的学生成绩管理系统 ,已经实现了增删改查,完美的springBoot项目脚手架 ,适合学习和二次开发,课程设计 已经实现了增删改查和搜索查询所有功能,完美的spring ...
- 基于 SpringBoot + Vue 的学生公寓管理系统
学生公寓管理系统 简介 基于 SpringBoot + Vue 的学生公寓管理系统,自定义了权限拦截器进行权限认证与授权,使用 aop+log4j 进行日志记录,使用 reids 作为缓存,使用 my ...
- 基于springboot的的学生干部管理系统
<基于springboot的的学生干部管理系统>该项目含有源码.论文等资料.配套开发软件.软件安装教程.项目发布教程等.系统功能完整,适合作为毕业设计.课程设计.数据库大作业学习使用. 项 ...
- java课件:基于springboot智慧校园后勤管理系统(springboot+vue+mysql+redis) 1017
项目描述 基于springboot智慧校园后勤管理系统, 包括管理员端,业务员端. 管理员主要功能:用户管理,角色管理,学生管理:职工管理:案件管理 等; 业务员主要功能:学生管理:职工管理:案件管理 ...
- java-net-php-python-springboot基于SpringBoot的OA办公管理系统计算机毕业设计程序
java-net-php-python-springboot基于SpringBoot的OA办公管理系统计算机毕业设计程序 java-net-php-python-springboot基于SpringB ...
- 计算机毕业设计springboot基于springboot的母婴服务管理系统qyh5j源码+系统+程序+lw文档+部署
计算机毕业设计springboot基于springboot的母婴服务管理系统qyh5j源码+系统+程序+lw文档+部署 计算机毕业设计springboot基于springboot的母婴服务管理系统qy ...
- 基于SPRINGBOOT的校企合作管理系统
开发工具(eclipse/idea): eclipse4.5/4.8或者idea2018,jdk1.8 数据库:mysql 功能模块: 基于springboot的校企合作管理系统,将用户分为三种权限: ...
- 基于SpringBoot的体育场馆运营管理系统的设计与实现
[摘要] 随着国家经济的告诉发展,人均生活水平在逐步提高,计算机网络技术的发展又在改变着人们生活.工作的方式.近一两年迎来了一股全民健身的热潮,像之前抖音直播间刘耕宏的健身操,带动了不少人的模仿.同时 ...
- 基于SpringBoot的城市建设用地管理系统的设计与实现
作者主页:Designer 小郑 作者简介:Java全栈软件工程师一枚,来自浙江宁波,负责开发管理公司OA项目,专注软件前后端开发(Vue.SpringBoot和微信小程序).系统定制.远程技术指导. ...
最新文章
- Python服务器开发一:python基础
- linux逻辑分区被删除了怎么办,找到了linux分区顺序错乱修复方法
- e会学中C语言课程考试答案,管理信息系统期末考试A试卷答案卷
- (转载)C#提取汉字拼音首字母的方法
- 新版开发工具?全新智能设备?华为开发者大会2021(Together)来了!
- AWK 高端大气上档次
- 博途PLC和ABB变频器PN通讯详解
- 基于STM32的高精度温度测控系统-PCB设计
- 安全狗又拿下一场重保胜战 第22届投洽会顺利谢幕
- 笔记:常见的约束问题求解算法——乘子法和Frank-Wolfe算法
- 利用Google Drive將英文版的PDF翻译成中文版的PDF
- Blender建模汇总
- 计算机之父图灵获英女王赦免
- 火车票能不能选座_终于,买火车票也能选座了!
- HBase的安装和使用
- 软考·系统架构师论文——论软件的高并发设计
- 业务异步写mysql数据库_把重要的业务日志异步批量写入数据库
- adobe Adobe Acrobat DC资源下载
- 易车上汽车报价准不准
- 数学知识—不同数据范围求组合数,例题、思路、代码实现