Skip to content

Commit 9cf2d81

Browse files
下载拦截器、自定义处理器 🔨
Former-commit-id: c06faa0ae1541af02637adc52659d792ee700617
1 parent ae0835f commit 9cf2d81

File tree

8 files changed

+179
-3
lines changed

8 files changed

+179
-3
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.geekidea.springbootplus.common.web.interceptor;
18+
19+
import lombok.extern.slf4j.Slf4j;
20+
import org.springframework.web.method.HandlerMethod;
21+
import org.springframework.web.servlet.ModelAndView;
22+
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
23+
24+
import javax.servlet.http.HttpServletRequest;
25+
import javax.servlet.http.HttpServletResponse;
26+
27+
/**
28+
* 下载拦截器
29+
* @author geekidea
30+
* @date 2019/8/21
31+
* @since 1.2.2-RELEASE
32+
*/
33+
@Slf4j
34+
public class DownloadInterceptor extends HandlerInterceptorAdapter {
35+
36+
@Override
37+
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
38+
// 如果访问的不是控制器,则跳出,继续执行下一个拦截器
39+
if (!(handler instanceof HandlerMethod)) {
40+
return true;
41+
}
42+
// 下载拦截器,业务处理代码
43+
log.info("DownloadInterceptor...");
44+
// 访问路径
45+
String url = request.getRequestURI();
46+
// 访问全路径
47+
String fullUrl = request.getRequestURL().toString();
48+
// 访问token,如果需要,可以设置参数,进行鉴权
49+
String token = request.getParameter("token");
50+
51+
log.info("url:{}",url);
52+
log.info("fullUrl:{}",fullUrl);
53+
log.info("token:{}",token);
54+
55+
return true;
56+
}
57+
58+
@Override
59+
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
60+
// 记录实际下载日志...
61+
}
62+
}

src/main/java/io/geekidea/springbootplus/config/WebMvcConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package io.geekidea.springbootplus.config;
1818

19+
import io.geekidea.springbootplus.common.web.interceptor.DownloadInterceptor;
1920
import io.geekidea.springbootplus.common.web.interceptor.PermissionInterceptor;
2021
import io.geekidea.springbootplus.common.web.interceptor.ResourceInterceptor;
2122
import io.geekidea.springbootplus.common.web.interceptor.TokenTimeoutInterceptor;
@@ -51,12 +52,19 @@ public class WebMvcConfig implements WebMvcConfigurer {
5152
@Autowired
5253
private ResourceInterceptor resourceInterceptor;
5354

55+
@Autowired
56+
private DownloadInterceptor downloadInterceptor;
57+
5458
@Override
5559
public void addInterceptors(InterceptorRegistry registry) {
5660
// 资源拦截器注册
5761
registry.addInterceptor(resourceInterceptor)
5862
.addPathPatterns(springBootPlusProperties.getInterceptorConfig().getResourceConfig().getIncludePath());
5963

64+
// 下载拦截器注册
65+
registry.addInterceptor(downloadInterceptor)
66+
.addPathPatterns(springBootPlusProperties.getInterceptorConfig().getDownloadConfig().getIncludePath());
67+
6068
// // JWT拦截器注册
6169
// registry.addInterceptor(jwtInterceptor)
6270
// .addPathPatterns("/**")

src/main/java/io/geekidea/springbootplus/config/core/SpringBootPlusConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.geekidea.springbootplus.config.core;
1717

1818
import io.geekidea.springbootplus.common.aop.LogAop;
19+
import io.geekidea.springbootplus.common.web.interceptor.DownloadInterceptor;
1920
import io.geekidea.springbootplus.common.web.interceptor.PermissionInterceptor;
2021
import io.geekidea.springbootplus.common.web.interceptor.ResourceInterceptor;
2122
import io.geekidea.springbootplus.common.web.interceptor.TokenTimeoutInterceptor;
@@ -90,4 +91,13 @@ public ResourceInterceptor resourceInterceptor(){
9091
return resourceInterceptor;
9192
}
9293

94+
/**
95+
* 下载拦截器
96+
* @return
97+
*/
98+
@Bean
99+
public DownloadInterceptor downloadInterceptor(){
100+
DownloadInterceptor downloadInterceptor = new DownloadInterceptor();
101+
return downloadInterceptor;
102+
}
93103
}

src/main/java/io/geekidea/springbootplus/config/core/SpringBootPlusInterceptorConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public class SpringBootPlusInterceptorConfig implements Serializable {
5050
*/
5151
private InterceptorConfig resourceConfig;
5252

53+
/**
54+
* 下载拦截器
55+
*/
56+
private InterceptorConfig downloadConfig;
57+
5358
@Data
5459
public static class InterceptorConfig {
5560

src/main/java/io/geekidea/springbootplus/config/core/SpringBootPlusProperties.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.springframework.boot.context.properties.ConfigurationProperties;
2222
import org.springframework.boot.context.properties.NestedConfigurationProperty;
2323

24+
import java.util.List;
25+
2426
/**
2527
* spring-boot-plus属性配置信息
2628
* @author geekidea
@@ -76,4 +78,13 @@ public class SpringBootPlusProperties {
7678
*/
7779
private String resourceAccessUrl;
7880

81+
/**
82+
* 允许上传的文件后缀集合
83+
*/
84+
private List<String> allowUploadFileExtensions;
85+
/**
86+
* 允许下载的文件后缀集合
87+
*/
88+
private List<String> allowDownloadFileExtensions;
89+
7990
}

src/main/java/io/geekidea/springbootplus/upload/web/DownloadController.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.web.bind.annotation.RequestMapping;
2828

2929
import javax.servlet.http.HttpServletResponse;
30+
import java.util.List;
3031

3132
/**
3233
* 下载控制器
@@ -48,7 +49,23 @@ public class DownloadController {
4849
@RequestMapping("/{downloadFileName}")
4950
@ApiOperation(value = "下载文件",notes = "下载文件",response = ApiResult.class)
5051
public void download(@PathVariable(required = true) String downloadFileName, HttpServletResponse response) throws Exception{
51-
DownloadUtil.download(springBootPlusProperties.getUploadPath(),downloadFileName,response);
52+
// 下载目录,既是上传目录
53+
String downloadDir = springBootPlusProperties.getUploadPath();
54+
// 允许下载的文件后缀
55+
List<String> allowFileExtensions = springBootPlusProperties.getAllowDownloadFileExtensions();
56+
// 文件下载,使用默认下载处理器
57+
// DownloadUtil.download(downloadDir,downloadFileName,allowFileExtensions,response);
58+
// 文件下载,使用自定义下载处理器
59+
DownloadUtil.download(downloadDir,downloadFileName,allowFileExtensions,response, (dir, fileName, file, fileExtension, contentType, length) -> {
60+
// 下载自定义处理,返回true:执行下载,false:取消下载
61+
System.out.println("dir = " + dir);
62+
System.out.println("fileName = " + fileName);
63+
System.out.println("file = " + file);
64+
System.out.println("fileExtension = " + fileExtension);
65+
System.out.println("contentType = " + contentType);
66+
System.out.println("length = " + length);
67+
return true;
68+
});
5269
}
5370

5471
}

src/main/java/io/geekidea/springbootplus/util/DownloadUtil.java

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@
1717
package io.geekidea.springbootplus.util;
1818

1919
import lombok.extern.slf4j.Slf4j;
20+
import org.apache.commons.io.FilenameUtils;
2021
import org.apache.commons.lang3.StringUtils;
2122
import org.springframework.util.Base64Utils;
23+
import org.springframework.util.CollectionUtils;
2224
import org.springframework.util.FileCopyUtils;
2325

2426
import javax.servlet.http.HttpServletRequest;
2527
import javax.servlet.http.HttpServletResponse;
2628
import java.io.*;
2729
import java.net.URLEncoder;
30+
import java.util.List;
2831

2932
/**
3033
* 文件下载工具类
@@ -36,12 +39,24 @@
3639
public final class DownloadUtil {
3740

3841
/**
39-
* 下载文件
42+
* 下载文件,使用默认下载处理器
43+
* @param downloadDir
44+
* @param downloadFileName
45+
* @param allowFileExtensions
46+
* @param response
47+
* @throws Exception
48+
*/
49+
public static void download( String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response) throws Exception{
50+
download(downloadDir,downloadFileName,allowFileExtensions,response,new DefaultDownloadHandler());
51+
}
52+
53+
/**
54+
* 下载文件,使用自定义下载处理器
4055
* @param downloadDir 文件目录
4156
* @param downloadFileName 文件名称
4257
* @throws Exception
4358
*/
44-
public static void download( String downloadDir, String downloadFileName,HttpServletResponse response) throws Exception {
59+
public static void download( String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response,DownloadHandler downloadHandler) throws Exception {
4560
log.info("downloadDir:{}",downloadDir);
4661
log.info("downloadFileName:{}",downloadFileName);
4762

@@ -51,6 +66,16 @@ public static void download( String downloadDir, String downloadFileName,HttpSer
5166
if (StringUtils.isBlank(downloadFileName)){
5267
throw new IOException("文件名称不能为空");
5368
}
69+
// 安全判断,防止../情况,防止出现类似非法文件名称:../../hello/123.txt
70+
if (downloadFileName.contains("..")||downloadFileName.contains("../")){
71+
throw new IOException("非法的文件名称");
72+
}
73+
// 允许下载的文件后缀判断
74+
if (CollectionUtils.isEmpty(allowFileExtensions)){
75+
throw new IllegalArgumentException("请设置允许下载的文件后缀");
76+
}
77+
// 获取文件名称
78+
String fileExtension = FilenameUtils.getExtension(downloadFileName);
5479

5580
// 从服务器读取文件,然后输出
5681
File downloadFile = new File(downloadDir,downloadFileName);
@@ -65,6 +90,17 @@ public static void download( String downloadDir, String downloadFileName,HttpSer
6590
long length = downloadFile.length();
6691
log.info("length:{}",length);
6792

93+
// 下载回调处理
94+
if (downloadHandler == null){
95+
// 使用默认下载处理器
96+
downloadHandler = new DefaultDownloadHandler();
97+
}
98+
boolean flag = downloadHandler.handle(downloadDir,downloadFileName,downloadFile,fileExtension,contentType,length);
99+
if (!flag){
100+
log.info("下载自定义校验失败,取消下载");
101+
return;
102+
}
103+
68104
// 下载文件名称编码,Firefox中文乱码处理
69105
String encodeDownFileName;
70106

@@ -78,6 +114,8 @@ public static void download( String downloadDir, String downloadFileName,HttpSer
78114
}
79115
log.info("encodeDownFileName:{}",encodeDownFileName);
80116

117+
log.info("下载文件:" + downloadFile.getAbsolutePath());
118+
81119
response.reset();
82120
// 设置Content-Disposition响应头
83121
response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodeDownFileName + "\"");
@@ -90,4 +128,24 @@ public static void download( String downloadDir, String downloadFileName,HttpSer
90128
InputStream in = new BufferedInputStream(new FileInputStream(downloadFile));
91129
FileCopyUtils.copy(in, response.getOutputStream());
92130
}
131+
132+
public static void main(String[] args) throws Exception {
133+
String downloadFileName = "../../hello/123.txt";
134+
// 安全判断,防止../情况
135+
if (downloadFileName.contains("..")||downloadFileName.contains("../")){
136+
throw new IOException("非法的文件名称");
137+
}
138+
System.out.println("ok");
139+
}
140+
141+
public static interface DownloadHandler{
142+
boolean handle(String dir, String fileName,File file,String fileExtension,String contentType,long length) throws Exception;
143+
}
144+
public static class DefaultDownloadHandler implements DownloadHandler{
145+
@Override
146+
public boolean handle(String dir, String fileName, File file, String fileExtension, String contentType, long length) throws Exception {
147+
return false;
148+
}
149+
}
150+
93151
}

src/main/resources/config/application.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ spring-boot-plus:
5656
exclude-path: /swagger-resources/**,/api-docs/**,/v2/api-docs/**,/docs,/resource/**
5757
resource-config:
5858
include-path: ${spring-boot-plus.resource-access-patterns}
59+
download-config:
60+
include-path: /download/**
5961
# 文件上传下载配置
6062
upload-path: /opt/upload/
6163
# 资源访问路径
@@ -64,6 +66,9 @@ spring-boot-plus:
6466
resource-access-patterns: ${spring-boot-plus.resource-access-path}**
6567
# 资源访问全路径前缀:http://localhost:8888/resource/
6668
resource-access-url: http://localhost:${server.port}${server.servlet.context-path}${spring-boot-plus.resource-access-path}
69+
# 全局允许上传的类型
70+
allow-upload-file-extensions: jpg,png,docx,xlsx,pptx,pdf
71+
allow-download-file-extensions: jpg,png,docx,xlsx,pptx,pdf
6772
############################### spring-boot-plus end ###############################
6873

6974

0 commit comments

Comments
 (0)