您的位置:首页 > 编程语言 > Java开发

企业提供下载链接的安全解决方案

2018-03-21 22:46 543 查看
        有时候我们在项目里面有需要提供给外界下载东西的链接(如先让用户下载我们给定的模板,然后让用户填完之后再上传)这时我们不得不对外开放我们的资源路径,如果用最简单的方式将项目内的资源路径暴露在页面供用户下载就涉及到了安全隐患,不法分子可能会根据我们提供的路径猜测到其他路径进行非法操作等等。

下面,介绍自己亲测可行的解决方案:
先贴上整个Demo的目录结构:



借用一个对url进行加密解密的工具类UrlUtil:package com.zy.web.common;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.SecureRandom;

public class UrlUtil {

private static final String KEY = "myYouMw6qPt&3AD";//这个KEY值可以任意指定,相当于一个密钥
private static final Logger LOGGER = LoggerFactory.getLogger(UrlUtil.class);

public static String enCryptAndEncode(String content) {
try {
byte[] sourceBytes = enCryptAndEncode(content, KEY);
return Base64.encodeBase64URLSafeString(sourceBytes);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
return content;
}
}

/**
* 加密函数
*
* @param content 加密的内容
* @param strKey 密钥
* @return 返回二进制字符数组
* @throws Exception
*/
public static byte[] enCryptAndEncode(String content, String strKey) throws Exception {

KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, new SecureRandom(strKey.getBytes()));

SecretKey desKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
return cipher.doFinal(content.getBytes("UTF-8"));
}

public static String deCryptAndDecode(String content) throws Exception {
byte[] targetBytes = Base64.decodeBase64(content);
return deCryptAndDecode(targetBytes, KEY);
}

/**
* 解密函数
*
* @param src 加密过的二进制字符数组
* @param strKey 密钥
* @return
* @throws Exception
*/
public static String deCryptAndDecode(byte[] src, String strKey) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128, new SecureRandom(strKey.getBytes()));

SecretKey desKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] cByte = cipher.doFinal(src);
return new String(cByte, "UTF-8");
}
}
web.xml中配置springmvc的两种常用的拦截方式 <!-- springMVC前端控制器 -->
<servlet>
<servlet-name>DownloadDemo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DownloadDemo</servlet-name>
<!-- 拦截所有请求jsp除外 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>DownloadDemo</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>在跳转到有下载链接的页面之前,在controller中进行如下处理:package com.zy.web.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zy.web.common.UrlUtil;

@Controller
@RequestMapping("/page")
public class PageController {

@RequestMapping("/toDownloadPage")
public String toDownloadPage(HttpServletRequest request){
String path = "/files/我的文件.docx";//这里指定页面上供用户下载的文件路径 我这里是从webapp目录下开始
String pathEn = UrlUtil.enCryptAndEncode(path);
request.setAttribute("path", pathEn);
return "download/downloadPage";
}

}
在有下载链接的页面中,该有链接的地方做如下处理:<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>下载页面</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/download/${path}">下载资源1</a>
<br/>
<a href="${pageContext.request.contextPath}/download1/test.action?url=${path}">下载资源2</a>
</body>
</html>启动项目后访问该页面效果如下:(亲测兼容IE7及以上)  这样就很难猜测项目的其他路径



然后当用户点击下载链接,后台controller应该这样处理:
Restfull风格的url对应的后台处理:package com.zy.web.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zy.web.common.UrlUtil;

@Controller
public class DownloadController{

private static final Logger logger = LoggerFactory.getLogger(DownloadController.class);

@RequestMapping("/download/{param}")
public void downloadFile(HttpServletRequest request,HttpServletResponse response,@PathVariable("param") String param){
InputStream fis = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
String decodeUrl = UrlUtil.deCryptAndDecode(param);
String fileName = decodeUrl.substring(decodeUrl.lastIndexOf("/") + 1);

response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentType("application/x-msdownload");

String filePath = request.getSession().getServletContext().getRealPath("") + decodeUrl;

File file = new File(filePath);
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(response.getOutputStream());
int len = 0;
byte[] bs = new byte[1024];
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bos.flush();
bos.close();
} catch (Exception e) {
logger.error("download file exception:",e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
logger.error("IO close exception:",e);
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
logger.error("IO close exception:",e);
}
}
}
}
}
常用的*.action或*.do的url对应的后台处理:package com.zy.web.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zy.web.common.UrlUtil;

@Controller
public class DownloadController1{

private static final Logger logger = LoggerFactory.getLogger(DownloadController1.class);

@RequestMapping("/download1/test")
public void downloadFile(HttpServletRequest request,HttpServletResponse response){
InputStream fis = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
String decodeUrl = UrlUtil.deCryptAndDecode(request.getParameter("url"));
String fileName = decodeUrl.substring(decodeUrl.lastIndexOf("/") + 1);

response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentType("application/x-msdownload");

String filePath = request.getSession().getServletContext().getRealPath("") + decodeUrl;

File file = new File(filePath);
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(response.getOutputStream());
int len = 0;
byte[] bs = new byte[1024];
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
bos.flush();
bos.close();
} catch (Exception e) {
logger.error("download file exception:",e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
logger.error("IO close exception:",e);
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
logger.error("IO close exception:",e);
}
}
}
}
}
这样就能让浏览器正确地下载到项目里面希望用户能够下载的资源而避免安全隐患了。(同时解决下载文件名为中文乱码问题)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐