文件上传加密
在很多应用场景中,出于安全考虑,我们不得不实行上传文件时对文件进行加密,
存入服务器的文件就会变成二进制文件,当别人直接冲服务器拿到文件时,也无法查看,这就保证了安全性。
但是我们需要在页面上查看自己上传的文件,这时候就需要再次请求服务器文件的解密接口,
通过解密代码,获得原来的图片,这样对于一些银行等相关业务可有效的保证安全性。
首先导入文件加密解密工具类
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
);
}
}
}