导航:
pre: 1.系统功能和架构介绍
next:3.用户登录
只挑重点的讲,具体的请看项目源码。
1.项目源码:
需要源码的朋友,请捐赠任意金额后留下邮箱发送:)
2.添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--SpringBoot热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!--JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--commons-lang3-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<!-- fonts file cannot use filter as the data structure of byte file will be changed via filter -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>static/layui/font/**</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
3.配置文件application.yml
server:
port: 8080
servlet:
context-path:
session:
timeout: 7200s
spring:
mvc:
static-path-pattern: /**
date-format: yyyy-MM-dd
favicon:
enabled: true
thymeleaf:
encoding: UTF-8
cache: false
mode: HTML
prefix: classpath:/templates/
suffix: .html
# 数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root123
url: jdbc:mysql://localhost:3306/bookman?characterEncoding=utf-8&useSSL=false
type: com.zaxxer.hikari.HikariDataSource
hikari:
pool-name: BookHikariPool
maximum-pool-size: 12
connection-timeout: 60000
connection-test-query: SELECT 1
servlet:
multipart:
max-request-size: 100MB
max-file-size: 50MB
# mybatis
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
# 文件上传路径
web:
upload:
path: D:/upload/
4.导入数据库
数据库脚本在:src/resources/bookman.sql
创建数据库:bookman 编码:UTF-8
执行SQL语句
5.工具类
5.1 文件上传
public class UploadUtils {
public static String upload(MultipartFile file, String path, String fileName) throws Exception {
// 生成新的文件名
String realPath = path + "/" + UUID.randomUUID().toString().replace("-", "") + fileName.substring(fileName.lastIndexOf("."));
File dest = new File(realPath);
// 判断文件父目录是否存在
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdir();
}
// 保存文件
file.transferTo(dest);
return dest.getName();
}
}
5.2 MD5
/**
* md5工具类
*/
public class MD5Util {
public static final int time = 5;
public static final String SALT = "springsecurity";
/**
* 密码加密方法
*
* @param password
* @return
*/
public static String encode(String password) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).");
}
try {
for (int i = 0; i < time; i++) {
byte[] bytes = digest.digest((password + SALT).getBytes("UTF-8"));
password = String.format("%032x", new BigInteger(1, bytes));
}
return password;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
}
}
public static void main(String[] args) {
System.out.println(MD5Util.encode("admin"));
}
}
5.3 spring security
/**
* @Description: spring security工具类
* @Author laoxu
* @Date 2019/5/11 17:20
**/
public class SecurityUtil {
public static String getLoginUser(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof AnonymousAuthenticationToken)) {
String currentUserName = authentication.getName();
return currentUserName;
}
return "";
}
}
6.配置类
6.1 MVC配置
/** mvc配置,例如:资源映射、视图解析、拦截器等
* @author laoxu
* @create 2018-10-23
**/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
SessionTimeoutInterceptor sessionTimeoutInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/assets/ckeditor/**").
addResourceLocations("classpath:/static/assets/ckeditor/").
setCachePeriod(2592000);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/success").setViewName("success");
registry.addViewController("/admin").setViewName("admin");
registry.addViewController("/book").setViewName("book");
registry.addViewController("/bookAdd").setViewName("bookAdd");
registry.addViewController("/bookEdit").setViewName("bookEdit");
registry.addViewController("/bookBorrow").setViewName("bookBorrow");
registry.addViewController("/bookBorrowAdd").setViewName("bookBorrowAdd");
registry.addViewController("/bookBorrowEdit").setViewName("bookBorrowEdit");
registry.addViewController("/bookReBorrow").setViewName("bookReBorrow");
registry.addViewController("/bookReBorrowEdit").setViewName("bookReBorrowEdit");
registry.addViewController("/bookReturn").setViewName("bookReturn");
registry.addViewController("/bookCategory").setViewName("bookCategory");
registry.addViewController("/bookLanguage").setViewName("bookLanguage");
registry.addViewController("/bookPublisher").setViewName("bookPublisher");
registry.addViewController("/bookShelf").setViewName("bookShelf");
registry.addViewController("/bookReader").setViewName("bookReader");
registry.addViewController("/bookStat").setViewName("bookStat");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(sessionTimeoutInterceptor).addPathPatterns("/**").
excludePathPatterns("/api/book/list","/bookDetail/*","/","/index","/login","/static/**","/logout");
}
}
6.2 统一返回结构
/**
* 请求返回结果
*
* @author laoxu
* @create 2018-10-23
**/
public class Result<T> {
private boolean success = true;
private int code = ErrorStatus.OK.getCode();
private String message = "";
private T data;
public static <T> Result<T> newInstance() {
return new Result<T>();
}
public Result() {
}
public Result(T data) {
this.data = data;
}
public Result(ErrorStatus status) {
this.message = status.getMessage();
this.code = status.getCode();
}
public Result(String message) {
this.message = message;
}
public Result(int code, String message) {
this.message = message;
this.code = code;
}
public Result(int code, String message, T data) {
this.message = message;
this.code = code;
this.data = data;
}
public Result<T> status(ErrorStatus status) {
this.message = message;
this.code = code;
return this;
}
public Result<T> ok() {
success = true;
return this;
}
public Result<T> fail() {
success = false;
return this;
}
public Result<T> message(String message) {
this.message = message;
return this;
}
public Result<T> data(T data) {
this.data = data;
return this;
}
public Result<T> code(int code) {
this.code = code;
return this;
}
public boolean isSuccess() {
return success;
}
// 省略get,set
}
/**
* @Description: 响应消息体
* @Author laoxu
* @Date 2019/12/21 9:50
**/
public class ResultBean<T> {
/**响应编码*/
private int code;
/**响应消息*/
private String msg;
/**数据总量*/
private int count;
/**数据*/
private T data;
public ResultBean() {
}
public ResultBean(int code, String msg, int count, T data) {
super();
this.code = code;
this.msg = msg;
this.count = count;
this.data = data;
}
@Override
public String toString() {
return "R [code=" + code + ", msg=" + msg + ", count=" + count + ", data=" + data + "]";
}
// 省略get,set
}
/**
* 返回结果工具类
*
* @author laoxu
* @create 2018-10-23
**/
public class ResultUtil {
public static boolean isOk(Result<?> result){
return null != result && result.getCode() == ErrorStatus.OK.getCode();
}
public static <T> Result<T> ok(){
return new Result<T>(ErrorStatus.OK);
}
public static <T> Result<T> ok(T data){
return new Result<T>(ErrorStatus.OK.getCode(), ErrorStatus.OK.getMessage(), data);
}
public static <T> Result<T> fail(){
return new Result<T>(ErrorStatus.BAD_REQUEST).fail();
}
public static <T> Result<T> status(ErrorStatus status){
return new Result<T>(status.getCode(), status.getMessage()).fail();
}
public static <T> Result<T> fail(ErrorStatus status){
return new Result<T>(status.getCode(), status.getMessage()).fail();
}
public static <T> Result<T> fail(String message){
return fail(ErrorStatus.BAD_REQUEST.getCode(), message, (T)null).fail();
}
public static <T> Result<T> fail(int code, String message){
return new Result<T>(code, message).fail();
}
public static <T> Result<T> fail(int code ,String message, T data){
return new Result<T>(code, message, data).fail();
}
public static <T> Result<T> notfound(){
return new Result<T>(ErrorStatus.NOT_FOUND).fail();
}
}
6.3 错误信息封装
/**
* 错误代号和信息
*
* @author laoxu
* @create 2018-10-23
**/
public enum ErrorStatus {
OK(200, "OK"),
FOUND(302, "Found"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
SERVICE_UNAVAILABLE(503, "Service Unavailable");
private final int code;
private final String message;
ErrorStatus(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
6.4 自定义异常
/**
* @Description: 异常处理器
* @Author laoxu
* @Date 2019/7/3 22:14
**/
@ControllerAdvice
public class ExceptionsHandler {
@ResponseBody
@ExceptionHandler(UnAuthorizedException.class)//可以直接写@ExceptionHandler,不指明异常类,会自动映射
public Result<String> customGenericExceptionHnadler(UnAuthorizedException exception){ //还可以声明接收其他任意参数
return ResultUtil.fail(Integer.valueOf(exception.getErrorCode()),exception.getErrorMessage());
}
}
7.静态资源
添加 layui文件