SpringBoot后端上传文件类型检测方式
作者:牟云飞
这篇文章主要介绍了SpringBoot后端上传文件类型检测方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
文件上传大部分通过web前端判断后尾名或者service后端判断后尾名,这种操作具有一定的风险,比如:我可以将一个jsp页面,修改后尾名改成jpg文件进行上传,由于图片预览功能,这个文件会被执行,这时就可以发送用户数据到指定的服务下,窃取用户信息。
本文通过文件流头部判断文件类型
不同的文件具有不同的头部,比如:
不同的文件具有不同的头部信息,以SpringBoot为例,通过拦截器拦截文件流进行判断:
1、添加配置文件checkFileHeader.properties
在src/main/resources中增加配置文件checkFileHeader.properties,文件内容:
JPEG=FFD8FF PNG=89504E47 GIF=47494638 TXT=75736167 PDF=255044462D312E DOC=D0CF11E0 XML=3C3F786D6C DOCX=504B0304 APK=504B030414000808 IPA=504B03040A000000
2、编写读取properties文件类
读取checkFileHeader.properties文件内容,用于拦截器判断
/** * 读取文件流头信息 * @author hanjie * */ public class FileHeaderHelper { private static FileHeaderHelper me ; private static List<String> headerList ; private FileHeaderHelper(){} public static FileHeaderHelper getInstance(){ if(me == null){ me = new FileHeaderHelper() ; } return me ; } public List<String> getHeaderList(){ if(headerList == null){ headerList = new ArrayList<String>() ; PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); String classpathResource = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/fileheader.properties"; Properties p = new Properties(); try { Resource[] res = resolver.getResources(classpathResource) ; for (Resource re : res) { p.load(re.getInputStream()); break ; } } catch (IOException e) { e.printStackTrace(); } for (Map.Entry<Object, Object> item : p.entrySet()) { headerList.add(item.getValue().toString()) ; } } return headerList ; } }
3、编写拦截器
拦截去中,获取文件流,读取文件流前8个字节,根据需要可以读取更多字节判读,8个字节转成16进制为16个字符串,我这里最长的APK/IPA文件也就16个字节,所以读取8个字节,读取字节后判断是否checkFileHeader.properties文件中字符串
/** * 文件上传拦截器 * @author hanjie * */ public class FileHeaderCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断是否为文件上传请求 if (request != null && request instanceof MultipartHttpServletRequest) { MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; Map<String, MultipartFile> files = multipartRequest.getFileMap(); Iterator<String> iterator = files.keySet().iterator(); while (iterator.hasNext()) { String formKey = (String) iterator.next(); MultipartFile multipartFile = multipartRequest.getFile(formKey); //String filename = multipartFile.getOriginalFilename(); byte[] file = multipartFile.getBytes() ; 获取字节流前8字节,差不多够了,不行再加 int HEADER_LENGTH = 8 ; if(file.length>HEADER_LENGTH){ //转成16进制 StringBuilder sb = new StringBuilder(); for(int i=0;i<HEADER_LENGTH;i++){ int v = file[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { sb.append(0); } sb.append(hv); } boolean isFound = false ; String fileHead = sb.toString().toUpperCase() ; List<String> headerList = FileHeaderHelper.getInstance().getHeaderList() ; for(String header : headerList){ if(fileHead.startsWith(header)){ isFound = true ; break ; } } if(!isFound){ // throw new BaseRunException("上传文件有异常,已被系统禁止!") ; System.out.println("----------上传文件有异常,已被系统禁止!头部信息:"+fileHead); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf-8"); PrintWriter printWriter = response.getWriter(); printWriter.write("上传文件有异常,已被系统禁止!"); return false; } } } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } }
4、配置拦截文件
拦截器写完了,配置下让它生效,在Configuration中配置拦截器,拦截文件流进行判断
@Configuration public class MyfWebAppConfiguration extends WebMvcConfigurerAdapter { //拦截器,拦截文件流 public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new FileHeaderCheckInterceptor()) .addPathPatterns("/**"); } // //注册过滤 // @Bean // public FilterRegistrationBean myFilterRegistration() { // // FilterRegistrationBean registration = new FilterRegistrationBean(); // registration.setFilter(new LoginFilter()); // registration.addUrlPatterns("/serviceInvoke"); // //registration.addInitParameter("paramName", "paramValue"); // registration.setName("loginFilter"); // registration.setOrder(1); // return registration; // } // // // //注册servlet // @Bean // public ServletRegistrationBean myServletRegistration() { // ServletRegistrationBean registration = new ServletRegistrationBean(new DownloadServlet()); // registration.addUrlMappings("/download"); // return registration; // } }
页面消息提醒已经在printWriter中输出了,根据自己的页面编写显示吧。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。