企业提供下载链接的安全解决方案
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);
}
}
}
}
}
这样就能让浏览器正确地下载到项目里面希望用户能够下载的资源而避免安全隐患了。(同时解决下载文件名为中文乱码问题)
下面,介绍自己亲测可行的解决方案:
先贴上整个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);
}
}
}
}
}
这样就能让浏览器正确地下载到项目里面希望用户能够下载的资源而避免安全隐患了。(同时解决下载文件名为中文乱码问题)
相关文章推荐
- 君安天下:为企业提供全方位的人员安全解决方案
- WCF搭建企业通用架构(创建安全的、可靠的、跨平台的的分布式解决方案)视频教程下载
- spring security 一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架
- 给力2011-东莞市南洋计算机软件有限公司 携手10大厂商提供企业安全有效节省成本解决方案
- iOS开发UI篇—推荐两个好用的Xcode插件(提供下载链接)
- ios app 企业帐号发布,在浏览器中直接点击链接下载安装
- 提供一段JavaScript脚本以辅助迅雷下载页面中的所有pdf文件链接
- 肖哥所有课程/HCNA HCNP/安全/云计算/linux 资料软件视频下载链接
- 在Delphi中实现类型安全的容器,Delphi泛型库DGL引介(提供源码下载)
- 重大消息,为企业、创业者提供直播解决方案的智播就要发布了
- 收集的网络上大型的开源图像处理软件代码(提供下载链接)
- web使用displaytag显示表格(提供排序,链接,下载等等)
- 登录一些安全网站,比如twitter/facebook,提示安全链接失败,或提示下载文件。
- Cocos2d-x简单游戏<植物大战僵尸>代码实现|第三部分:通关场景<后续会提供源码下载链接>
- 以小博大,小企业如何面对微软提供完整的解决方案
- Unity3D 3.5.2 MAC安装与破解&解决MonoDevelop Mac下乱码问题[提供下载链接]
- 微软正式提供Visual Studio 2013正式版下载(附直接链接汇总)