基本介绍
1.在Spring Boot项目中, 拦截器是开发中常用手段, 要来做登陆验证, 性能检查, 日志记录
等.
2.基本步骤:
√ 编写一个拦截器实现HandlerInterceptor
接口
√ 拦截器注册到配置类中(实现WebMvcConfigurer
的addInterceptors
)
√ 指定拦截器规则
√ 回顾SpringMVC中写到的Interceptor: SpringMVC系列十一: 文件上传与自定义拦截器
拦截器应用实例
需求分析
需求: 使用拦截器防止用户非法登录. 使用拦截器就不需要在每个方法验证了.
●浏览器输入 http://localhost:8080/manage.html, 如果用户没有登录, 则返回登录页面
1.修改AdminController.java
@Controller
@Slf4j
public class AdminController {
//响应用户的登录请求
@PostMapping("/login")
public String login(Admin admin, HttpSession session, Model model) {
//...
}
//处理用户请求 manage.html
@GetMapping("/manage.html")
public String mainPage(Model model,
@SessionAttribute(value = "loginAdmin", required = false) Admin admin) {
//这里暂时在方法中验证, 后面我们统一使用拦截器
log.info("进入mainPage()");
//用集合模拟用户数据, 放入到request域中, 并显示
List<User> users = new ArrayList<>();
users.add(new User(1, "张三", "123456", 23, "张三@163.com"));
users.add(new User(2, "李四", "123456", 24, "李四@163.com"));
users.add(new User(3, "王五", "123456", 25, "王五@163.com"));
users.add(new User(4, "赵六", "123456", 26, "赵六@163.com"));
users.add(new User(5, "田七", "123456", 27, "田七@163.com"));
//将数据放入到request域中
model.addAttribute("users", users);
return "manage";//这里是我们的视图解析器,到 templates/manage.html
}
}
2.修改 templates/manage.html
, 去掉 [[${session.loginAdmin.name}]]
, 大体代码如下.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>管理后台</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<a href='#'>返回管理界面</a> <a href='#' th:href="@{/}">安全退出</a> 欢迎您:
<hr/>
<div style="text-align: center">
<h1>管理雇员~</h1>
<table border="1px" cellspacing="0" bordercolor="green" style="width:800px;margin: auto">
<tr bgcolor="pink">
<td>id</td>
<td>name</td>
<td>pwd</td>
<td>age</td>
<td>email</td>
</tr>
<tr bgcolor="#ffc0cb" th:each="user:${users}">
<td th:text="${user.id}">a</td>
<td th:text="${user.name}">b</td>
<td th:text="${user.password}">c</td>
<td th:text="${user.age}">d</td>
<td th:text="${user.email}">e</td>
</tr>
</table>
<br/>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
3.浏览器/Postman, 能够访问 http://localhost:8080/manage.html
代码实现
1.创建src/main/java/com/zzw/springboot/interceptor/LoginInterceptor.java
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//为了让你们看到访问的URI
String requestURI = request.getRequestURI();
log.info("preHandle拦截到的请求的URI={}", requestURI);
//进行登录的验证
HttpSession session = request.getSession();
Object loginAdmin = session.getAttribute("loginAdmin");
if (loginAdmin != null) {//说明该用户已经成功登录
//放行
return true;
}
//拦截, 重新返回登陆页面
request.setAttribute("error", "你没有登录, 请重新登录~");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle() 被执行...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion() 被执行...");
}
}
2.注册拦截器到配置类中(实现WebMvcConfigurer
接口的addInterceptors
方法)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器 LoginInterceptor
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截所有的请求
.excludePathPatterns("/", "/login","/images/**"); //指定要放行的, 后面可以根据业务需求, 添加放行的请求路径
}
}
3.测试
注意事项和细节
1.将/images/**
去掉会有什么后果.
1)首先, 浏览器禁用缓存, 重启(是为了去掉保存在session里的登录信息).
2)其次, 刷新页面 http://localhost:8080/, 图片请求被拦截.
3)因为static
是类路径下, 所谓的静态资源访问, 所以可以写成images/**
, 而不用写成static/images/**
4)在前端代码中, 图片url
可以写成<img src="images/1.GIF">
, <img src="images/logo.png">
2.URI
和URL
的区别. HttpServletRequest常用方法
URI: Universal Resource Identifier
URL: Universal Resource Locator
Identifier
: 标识符. Locator
: 定位器, 从字面上来看, URI
可以在站点内唯一标识一个资源, URL
可以在全网内提供找到该资源的路径.
举例
String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();
log.info("preHandle拦截到的请求的URI={}", requestURI);// /manage.html
log.info("preHandle拦截到的请求的URL={}", requestURL);// http://localhost:8080/manage.html
3.注册拦截器, 依然可以使用如下方式, 参考注册自定义转换器
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
/*
class WebConfig2$1 implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器 LoginInterceptor
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截所有的请求
.excludePathPatterns("/", "/login", "/images/**"); //指定要放行的, 后面可以根据业务需求, 添加放行的请求路径
}
}
WebMvcConfigurer webMvcConfigurer = new WebConfig2$1();
return webMvcConfigurer;
*/
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("~~~~~匿名内部类注册拦截器~~~~~");
//注册自定义拦截器 LoginInterceptor
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截所有的请求
.excludePathPatterns("/", "/login", "/images/**"); //指定要放行的, 后面可以根据业务需求, 添加放行的请求路径
}
};
}
}
文件上传
需求说明
需求: 演示在SpringBoot 中通过表单注册用户, 并支持上传图片.
代码实现
1.创建templates/upload.html
, 要求头像只能选择一个, 而宠物可以上传多张图片
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF">
<div style="text-align: center;">
<h1>注册用户~</h1>
<form action="#" th:action="@{/upload}" method="post" enctype="multipart/form-data">
用户名:<input type="text" style="width: 150px" name="username"><br/><br/>
邮 件:<input type="text" style="width: 150px" name="email"><br/><br/>
年 龄:<input type="text" style="width: 150px" name="age"><br/><br/>
职 位:<input type="text" style="width: 150px" name="job"><br/><br/>
头 像:<input type="file" style="width: 150px" name="avatar"><br/><br/>
宠 物:<input type="file" style="width: 150px" name="pets" multiple><br/><br/>
<input type="submit" value="注册"/>
<input type="reset" value="重新填写">
</form>
</div>
<img src="images/logo.png">
</body>
</html>
2.指定拦截器放行upload.html
. upload
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("~~~~~匿名内部类注册拦截器~~~~~");
//注册自定义拦截器 LoginInterceptor
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截所有的请求
.excludePathPatterns("/", "/login", "/images/**", "/upload.html", "/upload"); //指定要放行的, 后面可以根据业务需求, 添加放行的请求路径
}
};
}
}
3.创建src/main/java/com/zzw/springboot/controller/UploadController.java
@Controller
public class UploadController {
//处理转发到用户注册页面upload.html-可以完成文件上传页面
@RequestMapping("/upload.html")
public String uploadPage() {
return "upload";//thymeleaf进行视图解析, 转发到templates/upload.html
}
}
4.浏览器访问 http://localhost:8080/upload.html, 测试
5.修改src/main/java/com/zzw/springboot/controller/UploadController.java
@Controller
@Slf4j
public class UploadController {
//处理转发到用户注册页面upload.html-可以完成文件上传页面
@RequestMapping("/upload.html")
public String uploadPage() {
return "upload";//thymeleaf进行视图解析, 转发到templates/upload.html
}
//处理用户的注册请求-包括处理文件上传
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam(value = "username") String name,
@RequestParam(value = "email") String email,
@RequestParam(value = "age") Integer age,
@RequestParam(value = "job") String job,
@RequestParam(value = "avatar") MultipartFile avatar,
@RequestParam(value = "pets") MultipartFile[] pets) {
//输出获取到的信息
log.info("上传的信息 name={}, email={}, age={}, job={}, avartar={}, pets={}",
name, email, age, job, avatar.getSize(), pets.length);
//如果信息都注册成功, 我们就将文件保存到指定的目录, 比如保存在d:\\temp_upload
//1.我们先将文件保存到指定到指定的目录, 比如d:\\temp_upload
//2.后面我们再演示把文件保存到动态创建的目录
return "用户注册成功/文件上传成功";
}
}
6.修改src/main/java/com/zzw/springboot/controller/UploadController.java
SpringMVC系列十一: 文件上传与自定义拦截器
@Controller
@Slf4j
public class UploadController {
//处理转发到用户注册页面upload.html-可以完成文件上传页面
@RequestMapping("/upload.html")
public String uploadPage() {
return "upload";//thymeleaf进行视图解析, 转发到templates/upload.html
}
//处理用户的注册请求-包括处理文件上传
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam(value = "username") String name,
@RequestParam(value = "email") String email,
@RequestParam(value = "age") Integer age,
@RequestParam(value = "job") String job,
@RequestParam(value = "avatar") MultipartFile avatar,
@RequestParam(value = "pets") MultipartFile[] pets) throws IOException {
//输出获取到的信息
log.info("上传的信息 name={}, email={}, age={}, job={}, avartar={}, pets={}",
name, email, age, job, avatar.getSize(), pets.length);
//如果信息都注册成功, 我们就将文件保存到指定的目录, 比如保存在d:\\temp_upload
//1.我们先将文件保存到指定到指定的目录, 比如d:\\temp_upload
//2.后面我们再演示把文件保存到动态创建的目录
if (!avatar.isEmpty()) {//处理头像上传
String originalFilename = avatar.getOriginalFilename();
log.info("你要上传的文件名={}", originalFilename);
//你要把文件上传到哪个路径[全路径: 包括文件名]
String fileFullPath = "d:\\temp_upload\\" + originalFilename;
File file = new File(fileFullPath);
avatar.transferTo(file);
}
if (pets.length > 0) {//处理宠物图片[多张]
for (MultipartFile pet : pets) {//循环遍历
if (!pet.isEmpty()) {
String originalFilename = pet.getOriginalFilename();//宠物图片名
log.info("你要上传的宠物名={}", originalFilename);
String fileFullPath = "d:\\temp_upload\\" + originalFilename;
File file = new File(fileFullPath);
pet.transferTo(file);
}
}
}
return "用户注册成功/文件上传成功";
}
}
7.测试
8.修改src/main/java/com/zzw/springboot/controller/UploadController.java
@Controller
@Slf4j
public class UploadController {
//处理转发到用户注册页面upload.html-可以完成文件上传页面
@RequestMapping("/upload.html")
public String uploadPage() {
return "upload";//thymeleaf进行视图解析, 转发到templates/upload.html
}
//处理用户的注册请求-包括处理文件上传
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam(value = "username") String name,
@RequestParam(value = "email") String email,
@RequestParam(value = "age") Integer age,
@RequestParam(value = "job") String job,
@RequestParam(value = "avatar") MultipartFile avatar,
@RequestParam(value = "pets") MultipartFile[] pets) throws IOException {
//输出获取到的信息
log.info("上传的信息 name={}, email={}, age={}, job={}, avartar={}, pets={}",
name, email, age, job, avatar.getSize(), pets.length);
//如果信息都注册成功, 我们就将文件保存到指定的目录, 比如保存在d:\\temp_upload
//1.我们先将文件保存到指定到指定的目录, 比如d:\\temp_upload
//2.后面我们再演示把文件保存到动态创建的目录
// 比如E:\idea_project\zzw_springboot\springboot-usersys\target\classes\static\images\\upload\
//得到类路径(运行的时候)
String path = ResourceUtils.getURL("classpath:").getPath();
//log.info("path={}", path);
//动态创建指定的目录
File parentFile = new File(path + "static/images/upload/");
if (!parentFile.exists()) {//如果目录不存在, 就创建 java io
parentFile.mkdirs();
}
if (!avatar.isEmpty()) {//处理头像上传
String originalFilename = avatar.getOriginalFilename();
log.info("你要上传的文件名={}", originalFilename);
//你要把文件上传到哪个路径[全路径: 包括文件名]
//String fileFullPath = "d:\\temp_upload\\" + originalFilename;
//File file = new File(fileFullPath);
//这里我们需要指定保存文件的绝对路径: E:\idea_project\zzw_springboot\springboot-usersys\target\classes\static\images\\upload\
//log.info("保存文件的绝对路径={}", parentFile.getAbsolutePath());
//保存到动态创建的目录
avatar.transferTo(new File(parentFile, originalFilename));
}
if (pets.length > 0) {//处理宠物图片[多张]
for (MultipartFile pet : pets) {//循环遍历
if (!pet.isEmpty()) {
String originalFilename = pet.getOriginalFilename();//宠物图片名
log.info("你要上传的宠物名={}", originalFilename);
//String fileFullPath = "d:\\temp_upload\\" + originalFilename;
//File file = new File(fileFullPath);
//保存到动态创建的目录
pet.transferTo(new File(parentFile, originalFilename));
}
}
}
return "用户注册成功/文件上传成功";
}
}
9.测试
注意事项和细节
1.根据项目需求修改文件上传的参数,否则文件上传会抛出异常。MultipartProperties.java
2.修改src/main/resources/application.yml
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
3.上传超大文件 8MB
课后扩展
1.解决文件覆盖问题, 如果文件名相同, 会出现覆盖问题, 如何解决.
2.解决文件分目录存放问题, 如果将文件都上传到一个目录下, 当上传文件很多时, 会造成访问文件速度变慢. 因此, 可以将文件上传到不同目录, 比如 一天上传的文件, 统一以年/月/日
的形式放到一个文件夹, 比如2024/8/5
目录.
3.参考 项目实战系列三: 家居购项目 第六部分
JavaWeb系列二十三: web 应用常用功能(文件上传下载)
代码实现
1.新建src/main/java/com/zzw/springboot/util/WebUtils.java
public class WebUtils {
//定义一个文件上传的路径
public static String UPLOAD_FILE_DIRECTORY = "static/images/upload/";
//编写方法, 生成一个目录-根据当前日期, 生成 年/月/日
public static String getUploadFileDirectory() {
LocalDate ld = LocalDate.now();
return ld.getYear() + "/" + ld.getMonthValue() + "/" + ld.getDayOfMonth() + "/";
}
}
2.修改src/main/java/com/zzw/springboot/controller/UploadController.java
@Controller
@Slf4j
public class UploadController {
//处理转发到用户注册页面upload.html-可以完成文件上传页面
@RequestMapping("/upload.html")
public String uploadPage() {
return "upload";//thymeleaf进行视图解析, 转发到templates/upload.html
}
//处理用户的注册请求-包括处理文件上传
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam(value = "username", required = false) String name,
@RequestParam(value = "email", required = false) String email,
@RequestParam(value = "age", required = false) Integer age,
@RequestParam(value = "job", required = false) String job,
@RequestParam(value = "avatar", required = false) MultipartFile avatar,
@RequestParam(value = "pets", required = false) MultipartFile[] pets) throws IOException {
//输出获取到的信息
log.info("上传的信息 name={}, email={}, age={}, job={}, avartar={}, pets={}",
name, email, age, job, avatar.getSize(), pets.length);
//如果信息都注册成功, 我们就将文件保存到指定的目录, 比如保存在d:\\temp_upload
//1.我们先将文件保存到指定到指定的目录, 比如d:\\temp_upload
//2.后面我们再演示把文件保存到动态创建的目录
// 比如E:\idea_project\zzw_springboot\springboot-usersys\target\classes\static\images\\upload\
//得到类路径(运行的时候)
String path = ResourceUtils.getURL("classpath:").getPath();
//log.info("path={}", path);
//动态创建指定的目录
File parentFile = new File(path + WebUtils.getUploadFileDirectory());
if (!parentFile.exists()) {//如果目录不存在, 就创建 java io
parentFile.mkdirs();
}
if (!avatar.isEmpty()) {//处理头像上传
String originalFilename = avatar.getOriginalFilename();
log.info("你要上传的文件名={}", originalFilename);
//对上传的文件名进行处理, 增加一个前缀, 保证是唯一的, 防止文件名重复造成覆盖
String fileName = UUID.randomUUID().toString() + "_" + System.currentTimeMillis() + "_" + originalFilename;
//你要把文件上传到哪个路径[全路径: 包括文件名]
//String fileFullPath = "d:\\temp_upload\\" + originalFilename;
//File file = new File(fileFullPath);
//这里我们需要指定保存文件的绝对路径: E:\idea_project\zzw_springboot\springboot-usersys\target\classes\static\images\\upload\
//log.info("保存文件的绝对路径={}", parentFile.getAbsolutePath());
//保存到动态创建的目录
File destPath = new File(parentFile, fileName);
if (!destPath.exists()) {
destPath.mkdirs();
}
avatar.transferTo(new File(destPath, fileName));
}
if (pets.length > 0) {//处理宠物图片[多张]
for (MultipartFile pet : pets) {//循环遍历
if (!pet.isEmpty()) {
String originalFilename = pet.getOriginalFilename();//宠物图片名
log.info("你要上传的宠物名={}", originalFilename);
//对上传的文件名进行处理, 增加一个前缀, 保证是唯一的, 防止文件名重复造成覆盖
String fileName = UUID.randomUUID().toString() + "_" + System.currentTimeMillis() + "_" + originalFilename;
//String fileFullPath = "d:\\temp_upload\\" + originalFilename;
//File file = new File(fileFullPath);
//保存到动态创建的目录
File destPath = new File(parentFile, fileName);
if (!destPath.exists()) {
destPath.mkdirs();
}
avatar.transferTo(new File(destPath, fileName));
}
}
}
return "用户注册成功/文件上传成功";
}
}