SpringBoot后端上传文件类型检测

文件上传大部分通过web前端判断后尾名或者service后端判断后尾名,这种操作具有一定的风险,比如:我可以将一个jsp页面,修改后尾名改成jpg文件进行上传,由于图片预览功能,这个文件会被执行,这时就可以发送用户数据到指定的服务下,窃取用户信息。本篇博文通过文件流头部判断文件类型,不同的文件具有不同的头部,比如:

JPEG文件:              FFD8FF
PNG文件:               89504E47
GIF文件:                 47494638
TXT文件:                75736167(txt、xml无固定头部,建议不允许上传)
PDF文件:                255044462D312E
DOC、XLS文件:     D0CF11E0
XML文件:                3C3F786D6C(txt、xml无固定头部,建议不允许上传)
DOCX、XLSX文件:504B0304
APK文件:                504B030414000808
IPA文件:                  504B03040A000000

不同的文件具有不同的头部信息,以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中输出了,根据自己的页面编写显示吧。

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值