文件上传加密
在很多应用场景中,出于安全考虑,我们不得不实行上传文件时对文件进行加密,
存入服务器的文件就会变成二进制文件,当别人直接冲服务器拿到文件时,也无法查看,这就保证了安全性。
但是我们需要在页面上查看自己上传的文件,这时候就需要再次请求服务器文件的解密接口,
通过解密代码,获得原来的图片,这样对于一些银行等相关业务可有效的保证安全性。
首先导入文件加密解密工具类
public class AES {
/**
* 块大小固定为8字节
*/
private final static String AES_CBC_PKCS5PADDING = "AES/ECB/PKCS5Padding";
/**
* KeyGenerator Init params
*/
private final static int KGEN_256 = 256;
private final static String SHA_256 = "SHA-256";
private final static String S_KEY = "9538172738539384";
private final static String SHA1PRNG = "SHA1PRNG";
// private static AlgorithmParameters params = null;
/**
* 字符串加密
* @param content
* @return
*/
public static String encrypt(String content) {
if (StringUtils.isNotBlank(content)) {
try {
byte[] byteContent = content.getBytes(Constant.ConfigConsts.ENCODE_UTF_8);
byte[] cryptograph = getCipher(Cipher.ENCRYPT_MODE, S_KEY).doFinal(byteContent);
return new Base64().encodeToString(cryptograph);
} catch (Exception e) {
e.printStackTrace();
}
}
return "";
}
/**
* 字符串解密
* @param content
* @return
*/
public static String decrypt(String content) {
if (StringUtils.isNotBlank(content)) {
try {
byte[] byteContent = new Base64().decode(content);
byte[] result = getCipher(Cipher.DECRYPT_MODE, S_KEY).doFinal(byteContent);
return new String(result);
} catch (Exception e) {
e.printStackTrace();
}
}
return "";
}
/**
* 通过Skey得到秘钥算法对象
* @param sKey
* @return
*/
private static SecretKeySpec getSecretKeySpec (String sKey) {
SecretKeySpec key = null;
try {
//"AES":请求的密钥算法的标准名称
KeyGenerator kgen = KeyGenerator.getInstance(Constant.ConfigConsts.SECRET_AES);
//256:密钥生成参数;securerandom:密钥生成器的随机源
SecureRandom securerandom = SecureRandom.getInstance(SHA1PRNG);
securerandom.setSeed(sKey.getBytes());
kgen.init(KGEN_256, securerandom);
//生成秘密(对称)密钥
SecretKey secretKey = kgen.generateKey();
//返回基本编码格式的密钥
byte[] enCodeFormat = secretKey.getEncoded();
//根据给定的字节数组构造一个密钥。enCodeFormat:密钥内容;"AES":与给定的密钥内容相关联的密钥算法的名称
key = new SecretKeySpec(enCodeFormat, Constant.ConfigConsts.SECRET_AES);
//将提供程序添加到下一个可用位置
Security.addProvider(new BouncyCastleProvider());
} catch (Exception e) {
e.printStackTrace();
}
return key;
}
/**
* get Cipher
* @param mode
* @param sKey
* @return
*/
private static Cipher getCipher(int mode, String sKey) {
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
Cipher cipher = null;
try {
// params = AlgorithmParameters.getInstance("IV", "LunaProvider");
// params.init(new IvParameterSpec(IV));
cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
cipher.init(mode, getSecretKeySpec(sKey));
} catch(Exception e) {
e.printStackTrace();
}
return cipher;
}
/**
* 上传文件加密(传入文件流,输出流对象后直接处理即加密文件流后存储文件)
* @param inputStream
* @param outputStream
* @return
*/
public static boolean encryptFile(InputStream inputStream, OutputStream outputStream) {
try {
CipherInputStream cipherInputStream = new CipherInputStream(
inputStream, getCipher(Cipher.ENCRYPT_MODE, S_KEY));
byte[] cache = new byte[1024];
int nRead = 0;
while ((nRead = cipherInputStream.read(cache)) != -1) {
outputStream.write(cache, 0, nRead);
outputStream.flush();
}
cipherInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* **上传文件加密(传入字节,加密后返回字节)**
* @param plainFile
* @return
* @throws Exception
*/
public static byte[] encryptFile(byte[] plainFile) throws Exception {
byte[] cipherText = getCipher(Cipher.ENCRYPT_MODE, S_KEY).doFinal(plainFile);
return cipherText;
}
/**
* 下载文件解密(传入文件流,输出流对象后直接处理即解密文件流后输出文件)
* @param inputStream
* @param outputStream
* @return
*/
public static boolean decryptFile(InputStream inputStream, OutputStream outputStream) {
try {
CipherOutputStream cipherOutputStream = new CipherOutputStream(
outputStream, getCipher(Cipher.DECRYPT_MODE, S_KEY));
byte[] buffer = new byte[1024];
int r;
while ((r = inputStream.read(buffer)) >= 0) {
cipherOutputStream.write(buffer, 0, r);
}
cipherOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 下载文件解密(传入字节,解密后返回字节)
* @param cipherFile
* @return
* @throws Exception
*/
public static byte[] decryptFile(byte[] cipherFile) throws Exception {
byte[] cipherText = getCipher(Cipher.DECRYPT_MODE, S_KEY).doFinal(cipherFile);
return cipherText;
}
/**
* 获得指定文件的byte数组
*/
private static byte[] getBytes(String filePath){
byte[] buffer = null;
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
/**
* 根据byte数组,生成文件
*/
public static void getFile(byte[] bfile, String filePath,String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
File dir = new File(filePath);
if(!dir.exists()&&dir.isDirectory()){ //判断文件目录是否存在
dir.mkdirs();
}
file = new File(filePath+"\\"+fileName);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bfile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
在这个工具方法中,我们使用 encryptFile(InputStream inputStream, OutputStream outputStream)对文件加密,在这个方法中需要把文件转成流的形式,并以byte【】的形式输出到指定位置。其中用到了类CipherInputStream,CipherInputStream由InputStream和Cipher组成,它允许我们自定义一个key,同时把key的信息揉进文件流中。解密的时候再次输入key,才能完成解密。
文件上传加密代码
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public RtnResult springUpload(HttpServletRequest request) throws IllegalStateException, IOException {
RtnResult result = new RtnResult(RtnResultCode.SUCCESS);
Map<String, String> map = new HashMap<>();
// 将request变成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
// 获取multiRequest 中所有的文件名
Iterator iter = multiRequest.getFileNames();
while (iter.hasNext()) {
//一次遍历所有文件
MultipartFile file = multiRequest.getFile(iter.next().toString());
if (file != null) {
// 取得当前上传文件的文件名称
String originalFileName = file.getOriginalFilename().replace(",", ";");
// 文件名前缀
int lastIndexOf = originalFileName.lastIndexOf(".");
String name = originalFileName;
String extension = "";
if (lastIndexOf != -1) {
name = originalFileName.substring(0, originalFileName.lastIndexOf("."));
extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
}
if (!extension.equalsIgnoreCase("png") && !extension.equalsIgnoreCase("jpg")
&& !extension.equalsIgnoreCase("jpeg") && !extension.equalsIgnoreCase("gif")
&& !extension.equalsIgnoreCase("pdf") && !extension.equalsIgnoreCase("xlsx")
&& !extension.equalsIgnoreCase("lsx") && !extension.equalsIgnoreCase("docx")
&& !extension.equalsIgnoreCase("doc")) {
return RtnResult.Fail(RtnResultCode.FILE_TYPE_ERROR);
}
// 时间戳
long timeStr = (new Date()).getTime();
// 4位随机数
int random = new Random().nextInt(10000);
// 文件后缀名
String fileName =timeStr + random + "." + extension;
//上传
String dateStr = DateUtils.getDateTime("yyyyMMdd");
//上传 ,替代掉常量类中的数据
String path = PathConstants.DIRECTORY_UPLOAD_TEMP_SUB
.replace("{yyyyMMdd}", dateStr)
.replace("{fileName}", fileName);
File temp = new File(configProperties.getFileLocation() + PathConstants.DIRECTORY_UPLOAD_TEMP + path);
if (!temp.getParentFile().exists()) {
temp.getParentFile().mkdirs();
}
//上传文件加密
OutputStream enOutputStream = new FileOutputStream(temp);
boolean b = AES.encryptFile(file.getInputStream(), enOutputStream);
if (!b){
return RtnResult.Fail("文件上传失败!");
}
logger.info("文件上传成功!------------------");
// file.transferTo(temp);
map.put("fileName", file.getOriginalFilename());
map.put("filePath", path);
String dowUrl = PathConstants.URL_FILE_TEMP_ORIGIN.replace("{date}", dateStr).replace("{fileName}", fileName);
map.put("downUrl", dowUrl);
}
}
result.setData(map);
return result;
}
下载文件解密
传入文件名,根据配置文件拼装出文件地址,通过new file(path) ,得到真实文件。通过HttpServletResponse.getOutputStream以及相关设置可以向页面输出文件内容,再调用机密方法即可
@RequestMapping(value = "/download/temp", method = RequestMethod.GET)
@ResponseBody
public void downloadTemp(@RequestParam("date") String date,@RequestParam("fileName") String fileName,HttpServletResponse response) throws IOException {
logger.info("FileController.downloadTemp=========>start");
if (StringUtils.isNotBlank(fileName) && StringUtils.isNotBlank(date)) {
String path =
configProperties.getFileLocation()+PathConstants.DIRECTORY_UPLOAD_TEMP +
PathConstants.DIRECTORY_UPLOAD_TEMP_SUB.replace("{yyyyMMdd}", date)
.replace("{fileName}", fileName);
//通过路径得到文件
File file = new File(path);
logger.info("FileController.downloadTemp=========File:{},exits:{}",path,file.exists());
if (file.exists()) {
//浏览器接受图片设置
String contentType = Files.probeContentType(Paths.get(path));
contentType = StringUtils.isBlank(contentType) ? MediaType.ALL_VALUE : contentType;
response.setContentType(contentType);
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(file.getName()));
response.setCharacterEncoding("UTF-8");
OutputStream outputStream = response.getOutputStream();
FileInputStream fis = FileUtils.openInputStream(file);
//文件解密
boolean b = AES.decryptFile(fis, outputStream);
if (b){
logger.info("文件解密成功-----------------------");
}else {
logger.info("文件解密失败!----------------------");
}
//IOUtils.copy(fis, outputStream);
outputStream.flush();
outputStream.close();
IOUtils.closeQuietly(fis);
}
}
}