This commit is contained in:
JSYOO 2021-12-21 17:39:10 +09:00
commit d381766124
4 changed files with 361 additions and 333 deletions

View File

@ -385,6 +385,13 @@
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<!-- OWASP HTML Sanitizer -->
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
<version>20211018.2</version>
</dependency>
</dependencies>

View File

@ -1,327 +1,322 @@
package nlib.cmm.fileupload;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import nlib.bbs.service.AttachFileService;
import nlib.bbs.service.AttachFileVO;
import nlib.cmm.NlibCommonController;
import nlib.cmm.exception.ErrorMessage;
import nlib.cmm.service.NlibProperty;
import nlib.util.FileUtil;
import nlib.util.StringUtil;
import nlib.util.UUID;
/**
* <pre>
* @Class Name : FileUploadController.java
*
* @Description : 파일 업로드를 처리하는 컨트롤러
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 7. 15. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 7. 15.
* @version 1.0
*
*/
@Controller
public class FileUploadController extends NlibCommonController {
private static final Logger log = LoggerFactory.getLogger(FileUploadController.class);
/**
* 첨부파일이 저장되는 최상위 위치
*/
private String FILEUPLOAD_BASE_PATH = NlibProperty.getProperty("fileupload.base.path");
/**
* 첨부파일 임시 저장 위치
*/
private String FILEUPLOAD_TEMP_SUBPATH = NlibProperty.getProperty("fileupload.temp.subpath");
/**
* 경로 부적합 오류 메시지
*/
private static final String MSG_NOT_VALID_FILE_PATH = "파일 경로가 부적합합니다. 관리자에게 문의하여 주시기 바랍니다.";
@Resource(name="attachFileService")
private AttachFileService attachFileService;
/**
* 파일업로드(Ajax) 처리
* - 화면 : Javascript Free Open Framework DropZone 사용
*
* @param multiRequest
* @param subPathKey
* @param response : 파일정보를 담은 JSON String
* @return
*/
@Secured("ROLE_USER")
@ResponseBody
@RequestMapping(value="/fileupload/uploadFilesAjax.do", produces="application/json")
public ResponseEntity uploadFilesAjax(
final MultipartHttpServletRequest multiRequest
, @RequestParam(name="subPathKey") String subPathKey
, HttpServletResponse response) {
String message = null;
// TODO : 권한 체크 추가할
List<AttachFileVO> result = new ArrayList<AttachFileVO>();
try {
// 최상위 위치
if(FileUtil.isEmpty(FILEUPLOAD_BASE_PATH)) {
message = "첨부파일이 저장되는 최상위 위치정보값이 정확하지 않습니다.";
log.error(message + " : FILEUPLOAD_BASE_PATH 값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 서브 위치
if(StringUtil.isEmpty(subPathKey)) {
message = "첨부파일이 저장되는 하위 위치정보값이 정확하지 않습니다.";
log.error(message + " : 매개변수 subPathKey 값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
String subPath = NlibProperty.getProperty(subPathKey);
if(StringUtil.isEmpty(subPath)) {
message = "첨부파일이 저장되는 하위 위치정보값을 찾을 수 없습니다.";
log.error(message + " : 매개변수 subPathKey의 속성값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 위치정보 적합 확인
if(FileUtil.isNotValid(FILEUPLOAD_BASE_PATH + subPath)) {
message = MSG_NOT_VALID_FILE_PATH;
log.error(message + " : " + (FILEUPLOAD_BASE_PATH + subPath));
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 파일 업로드 처리
final Map<String, MultipartFile> files = multiRequest.getFileMap();
log.debug("uploadFilesAjax : 진입 > 파일수 = " + (files == null ? " files null " : files.size()));
File dir = new File(FILEUPLOAD_BASE_PATH + subPath);
if(!dir.exists() || !dir.isDirectory()) dir.mkdirs();
if(!files.isEmpty()) {
Iterator<Entry<String, MultipartFile>> itr = files.entrySet().iterator();
MultipartFile file;
String filePath = "";
AttachFileVO atvo;
// 허용 파일 확장자
String acceptableExtList = StringUtil.getString(NlibProperty.getString("fileupload.acceptable.ext"), null);
if(acceptableExtList != null) acceptableExtList = "," + acceptableExtList.trim().toLowerCase();
boolean acceptableExt = false;
// 최대 파일크기
int maxFileSize = NlibProperty.getInt("fileupload.max.mbsize", 10) * 1024 * 1024;
// 최대 개수
int maxFileCount = NlibProperty.getInt("fileupload.max.files", 5);
while (itr.hasNext()) {
if(result.size() >= maxFileCount) {
log.error("최대 개수를 초과하여 이후 파일은 처리되지 않습니다.");
break;
}
acceptableExt = false;
Entry<String, MultipartFile> entry = itr.next();
file = entry.getValue();
String orignlFileNm = file.getOriginalFilename();
//--------------------------------------
// 파일명이 없는 경우 처리 SKIP
// (첨부가 되지 않은 input file type)
//--------------------------------------
if (orignlFileNm == null || "".equals(orignlFileNm)) {
continue;
}
// 파일 확장자
int index = orignlFileNm.lastIndexOf(".");
String fileExt = (index < 1 ? "" : orignlFileNm.substring(index + 1)).toLowerCase();
if(acceptableExtList != null) {
if(acceptableExtList.contains("." + fileExt)) {
acceptableExt = true;
} else if(acceptableExtList.contains("image/*")) {
acceptableExt = ".jpg,.jpe,.jpge,.bmp,.gif,.png".contains("." + fileExt);
}
}
if(!acceptableExt) {
log.error("허용되지 않는 파일 확장자 입니다. : " + orignlFileNm);
continue;
}
int size = (int)file.getSize();
if(size > maxFileSize) {
log.error("첨부파일이 허용 크기를 초과합니다. : " + orignlFileNm);
continue;
}
String streFileNm = UUID.getNewStreFileNm();
filePath = FILEUPLOAD_BASE_PATH + subPath + "/" + streFileNm;
log.debug("FILE UPLOAD : filePath=" + filePath);
file.transferTo(new File(FileUtil.filePathBlackList(filePath)));
atvo = new AttachFileVO();
atvo.setSubPathKey(subPathKey);
//atvo.setAttachFileId(attachFileId); // 실제 DB 저장 처리될때 설정함
atvo.setStreFileNm(streFileNm);
atvo.setOrignlFileNm(orignlFileNm);
atvo.setFileExt(fileExt);
atvo.setFileSize(size);
result.add(atvo);
}
}
} catch(SecurityException e) {
log.error("uploadFilesAjax SecurityException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
} catch(IllegalStateException e) {
log.error("uploadFilesAjax IllegalStateException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
} catch(IOException e) {
log.error("uploadFilesAjax IOException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// Convert Json
ObjectMapper mapper = new ObjectMapper();
String json;
try {
json = mapper.writeValueAsString(result);
log.debug("RETURN DATA > json:" + json);
} catch (JsonProcessingException e) {
json = null;
log.error("uploadFilesAjax Error : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "JSON 변환 처리에 실패하였습니다 : " + e.toString());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
HttpHeaders responseHeaders = new HttpHeaders();
ResponseEntity ret = ResponseEntity.ok().headers(responseHeaders).body(json);
return ret;
}
/**
* 파일 다운로드를 처리한다.
*
* @param params
* @param mv
* @return
* @throws Exception
*/
@RequestMapping(value="/fileupload/downloadAttachFile.do")
public ModelAndView downloadAttachFile(HttpServletRequest request, AttachFileVO attachFileVO, ModelAndView mv) throws Exception {
//---------------------------------
// 권한체크
//---------------------------------
// 공지사항, FAQ : 모두 공개
// QnA : 공개된 글인 경우, 모두 공개
// QnA : 비밀글 (본인에 한정)
attachFileVO.setMbInfoId(getMbInfoId(request));
boolean hasAuth = attachFileService.checkAttachFileDownload(attachFileVO);
if(!hasAuth) {
throw new Exception("잘못된 접근입니다. 파일다운로드를 할 수 있는 권한이 없습니다.");
}
AttachFileVO retAttachFileVO = attachFileService.selectAttachFile(attachFileVO);
if(retAttachFileVO == null || StringUtil.isEmpty(retAttachFileVO.getAttachFileId())) {
throw new Exception("잘못된 접근이므로 파일다운로드를 할 수 없습니다.");
}
if(StringUtil.isEmpty(retAttachFileVO.getFileStrePath()) || StringUtil.isEmpty(retAttachFileVO.getStreFileNm())) {
throw new Exception("파일정보가 올바르지 않습니다.");
}
if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
}
if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
}
String fullPath = retAttachFileVO.getFileStrePath() + retAttachFileVO.getStreFileNm();
log.debug("downloadAttachFile > fullPath = " + fullPath);
if(FileUtil.isNotValid(fullPath)) {
throw new Exception(MSG_NOT_VALID_FILE_PATH);
}
File file = new File(FileUtil.filePathBlackList(fullPath));
if(!file.isFile()) {
throw new Exception("해당 파일이 존재하지 않습니다. 관리자에게 문의하여 주시기 바랍니다.");
}
mv.setViewName("downloadView"); // dispatcher-servlet.xml내 BeanNameViewResolver 정의
mv.addObject("downloadFile", file);
mv.addObject("fname", retAttachFileVO.getOrignlFileNm());
return mv;
}
}
package nlib.cmm.fileupload;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import nlib.bbs.service.AttachFileService;
import nlib.bbs.service.AttachFileVO;
import nlib.cmm.NlibCommonController;
import nlib.cmm.exception.ErrorMessage;
import nlib.cmm.service.NlibProperty;
import nlib.util.FileUtil;
import nlib.util.StringUtil;
import nlib.util.UUID;
/**
* <pre>
* @Class Name : FileUploadController.java
*
* @Description : 파일 업로드를 처리하는 컨트롤러
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 7. 15. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 7. 15.
* @version 1.0
*
*/
@Controller
public class FileUploadController extends NlibCommonController {
private static final Logger log = LoggerFactory.getLogger(FileUploadController.class);
/**
* 첨부파일이 저장되는 최상위 위치
*/
private String FILEUPLOAD_BASE_PATH = NlibProperty.getProperty("fileupload.base.path");
/**
* 경로 부적합 오류 메시지
*/
private static final String MSG_NOT_VALID_FILE_PATH = "파일 경로가 부적합합니다. 관리자에게 문의하여 주시기 바랍니다.";
@Resource(name="attachFileService")
private AttachFileService attachFileService;
/**
* 파일업로드(Ajax) 처리
* - 화면 : Javascript Free Open Framework DropZone 사용
*
* @param multiRequest
* @param subPathKey
* @param response : 파일정보를 담은 JSON String
* @return
*/
@Secured("ROLE_USER")
@ResponseBody
@RequestMapping(value="/fileupload/uploadFilesAjax.do", produces="application/json")
public ResponseEntity uploadFilesAjax(
final MultipartHttpServletRequest multiRequest
, @RequestParam(name="subPathKey") String subPathKey
, HttpServletResponse response) {
String message = null;
// TODO : 권한 체크 추가할
List<AttachFileVO> result = new ArrayList<AttachFileVO>();
try {
// 최상위 위치
if(FileUtil.isEmpty(FILEUPLOAD_BASE_PATH)) {
message = "첨부파일이 저장되는 최상위 위치정보값이 정확하지 않습니다.";
log.error(message + " : FILEUPLOAD_BASE_PATH 값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 서브 위치
if(StringUtil.isEmpty(subPathKey)) {
message = "첨부파일이 저장되는 하위 위치정보값이 정확하지 않습니다.";
log.error(message + " : 매개변수 subPathKey 값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
String subPath = NlibProperty.getProperty(subPathKey);
if(StringUtil.isEmpty(subPath)) {
message = "첨부파일이 저장되는 하위 위치정보값을 찾을 수 없습니다.";
log.error(message + " : 매개변수 subPathKey의 속성값 부재");
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 위치정보 적합 확인
if(FileUtil.isNotValid(FILEUPLOAD_BASE_PATH + subPath)) {
message = MSG_NOT_VALID_FILE_PATH;
log.error(message + " : " + (FILEUPLOAD_BASE_PATH + subPath));
ErrorMessage errorMessage = new ErrorMessage("ERROR", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// 파일 업로드 처리
final Map<String, MultipartFile> files = multiRequest.getFileMap();
log.debug("uploadFilesAjax : 진입 > 파일수 = " + (files == null ? " files null " : files.size()));
File dir = new File(FILEUPLOAD_BASE_PATH + subPath);
if(!dir.exists() || !dir.isDirectory()) dir.mkdirs();
if(!files.isEmpty()) {
Iterator<Entry<String, MultipartFile>> itr = files.entrySet().iterator();
MultipartFile file;
String filePath = "";
AttachFileVO atvo;
// 허용 파일 확장자
String acceptableExtList = StringUtil.getString(NlibProperty.getString("fileupload.acceptable.ext"), null);
if(acceptableExtList != null) acceptableExtList = "," + acceptableExtList.trim().toLowerCase();
boolean acceptableExt = false;
// 최대 파일크기
int maxFileSize = NlibProperty.getInt("fileupload.max.mbsize", 10) * 1024 * 1024;
// 최대 개수
int maxFileCount = NlibProperty.getInt("fileupload.max.files", 5);
while (itr.hasNext()) {
if(result.size() >= maxFileCount) {
log.error("최대 개수를 초과하여 이후 파일은 처리되지 않습니다.");
break;
}
acceptableExt = false;
Entry<String, MultipartFile> entry = itr.next();
file = entry.getValue();
String orignlFileNm = file.getOriginalFilename();
//--------------------------------------
// 파일명이 없는 경우 처리 SKIP
// (첨부가 되지 않은 input file type)
//--------------------------------------
if (orignlFileNm == null || "".equals(orignlFileNm)) {
continue;
}
// 파일 확장자
int index = orignlFileNm.lastIndexOf(".");
String fileExt = (index < 1 ? "" : orignlFileNm.substring(index + 1)).toLowerCase();
if(acceptableExtList != null) {
if(acceptableExtList.contains("." + fileExt)) {
acceptableExt = true;
} else if(acceptableExtList.contains("image/*")) {
acceptableExt = ".jpg,.jpe,.jpge,.bmp,.gif,.png".contains("." + fileExt);
}
}
if(!acceptableExt) {
log.error("허용되지 않는 파일 확장자 입니다. : " + orignlFileNm);
continue;
}
int size = (int)file.getSize();
if(size > maxFileSize) {
log.error("첨부파일이 허용 크기를 초과합니다. : " + orignlFileNm);
continue;
}
String streFileNm = UUID.getNewStreFileNm();
filePath = FILEUPLOAD_BASE_PATH + subPath + "/" + streFileNm;
log.debug("FILE UPLOAD : filePath=" + filePath);
file.transferTo(new File(FileUtil.filePathBlackList(filePath)));
atvo = new AttachFileVO();
atvo.setSubPathKey(subPathKey);
//atvo.setAttachFileId(attachFileId); // 실제 DB 저장 처리될때 설정함
atvo.setStreFileNm(streFileNm);
atvo.setOrignlFileNm(orignlFileNm);
atvo.setFileExt(fileExt);
atvo.setFileSize(size);
result.add(atvo);
}
}
} catch(SecurityException e) {
log.error("uploadFilesAjax SecurityException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
} catch(IllegalStateException e) {
log.error("uploadFilesAjax IllegalStateException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
} catch(IOException e) {
log.error("uploadFilesAjax IOException : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "처리에 실패하였습니다(1)");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
// Convert Json
ObjectMapper mapper = new ObjectMapper();
String json;
try {
json = mapper.writeValueAsString(result);
log.debug("RETURN DATA > json:" + json);
} catch (JsonProcessingException e) {
json = null;
log.error("uploadFilesAjax Error : " + e.toString());
ErrorMessage errorMessage = new ErrorMessage("ERROR", "JSON 변환 처리에 실패하였습니다 : " + e.toString());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorMessage);
}
HttpHeaders responseHeaders = new HttpHeaders();
ResponseEntity ret = ResponseEntity.ok().headers(responseHeaders).body(json);
return ret;
}
/**
* 파일 다운로드를 처리한다.
*
* @param params
* @param mv
* @return
* @throws Exception
*/
@RequestMapping(value="/fileupload/downloadAttachFile.do")
public ModelAndView downloadAttachFile(HttpServletRequest request, AttachFileVO attachFileVO, ModelAndView mv) throws Exception {
//---------------------------------
// 권한체크
//---------------------------------
// 공지사항, FAQ : 모두 공개
// QnA : 공개된 글인 경우, 모두 공개
// QnA : 비밀글 (본인에 한정)
attachFileVO.setMbInfoId(getMbInfoId(request));
boolean hasAuth = attachFileService.checkAttachFileDownload(attachFileVO);
if(!hasAuth) {
throw new Exception("잘못된 접근입니다. 파일다운로드를 할 수 있는 권한이 없습니다.");
}
AttachFileVO retAttachFileVO = attachFileService.selectAttachFile(attachFileVO);
if(retAttachFileVO == null || StringUtil.isEmpty(retAttachFileVO.getAttachFileId())) {
throw new Exception("잘못된 접근이므로 파일다운로드를 할 수 없습니다.");
}
if(StringUtil.isEmpty(retAttachFileVO.getFileStrePath()) || StringUtil.isEmpty(retAttachFileVO.getStreFileNm())) {
throw new Exception("파일정보가 올바르지 않습니다.");
}
if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
}
if(StringUtil.isEmpty(retAttachFileVO.getOrignlFileNm())) {
retAttachFileVO.setOrignlFileNm("첨부파일." + retAttachFileVO.getFileExt());
}
String fullPath = retAttachFileVO.getFileStrePath() + retAttachFileVO.getStreFileNm();
log.debug("downloadAttachFile > fullPath = " + fullPath);
if(FileUtil.isNotValid(fullPath)) {
throw new Exception(MSG_NOT_VALID_FILE_PATH);
}
File file = new File(FileUtil.filePathBlackList(fullPath));
if(!file.isFile()) {
throw new Exception("해당 파일이 존재하지 않습니다. 관리자에게 문의하여 주시기 바랍니다.");
}
mv.setViewName("downloadView"); // dispatcher-servlet.xml내 BeanNameViewResolver 정의
mv.addObject("downloadFile", file);
mv.addObject("fname", retAttachFileVO.getOrignlFileNm());
return mv;
}
}

View File

@ -13,6 +13,8 @@ import java.util.Base64.Encoder;
import java.util.Locale;
import org.apache.commons.lang3.StringUtils;
import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.PolicyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -383,6 +385,33 @@ public class StringUtil extends StringUtils {
return value;
}
/**
* XSS 등의 공격으로 부터 보안성 유지를 위해서 HTML 허용된 태그와 속성만으로 HTML을 재구성하여 리턴한다.
*
* @param html
* @return
*/
private static String sanitizeHtml(String html) {
PolicyFactory policy = new HtmlPolicyBuilder()
.allowAttributes("src", "align", "title").onElements("img")
.allowAttributes("href", "title").onElements("a")
.allowAttributes("class", "height", "width", "style").globally()
.allowUrlProtocols("http","https","mailto","tel")
.allowElements(
"a", "label",
"h1", "h2", "h3", "h4", "h5", "h6",
"p", "i", "b", "u", "strong", "em", "small", "big", "pre", "code",
"cite", "samp", "sub", "sup", "strike", "center", "blockquote",
"hr", "br", "col", "font", "span", "div", "img",
"ul", "ol", "li", "dd", "dt", "dl", "tbody", "thead", "tfoot",
"table", "td", "th", "tr", "colgroup", "fieldset", "legend"
)
.toFactory();
return policy.sanitize(html);
}
// Tag 화이트 리스트 ( 허용할 태그 등록 )
static String[] whiteListTag = { "<p>","</p>","<br />" };

View File

@ -108,6 +108,9 @@ fileupload.base.path = /nfs_nas_dev/uac/nlib
# \uc6b4\uc601\uc11c\ubc84
#fileupload.base.path = /nfs_nas/uac/nlib
# \ubb3b\uace0\ub2f5\ud558\uae30 \uac8c\uc2dc\ud310 \uc11c\ube0c\uc704\uce58 : \ucca8\ubd80\ud30c\uc77c \ucd5c\uc0c1\uc704 \uc704\uce58 \uc774\ud558\uc758 \uc704\uce58 \uc815\ubcf4\ub97c \uc124\uc815
fileupload.bbs.qna.subpath = /qna
# \ucd5c\ub300 \ud30c\uc77c \ud06c\uae30 (MB)
fileupload.max.mbsize = 10
# \ucd5c\ub300 \ud30c\uc77c \uac1c\uc218
@ -116,12 +119,6 @@ fileupload.max.files = 5
fileupload.acceptable.ext = .jpg,.jpe,.jpge,.bmp,.gif,.png,.hwp,.hwpx,.pdf,.xls,.xlsx,.ppt,.pptx,.doc,.docx,.txt
# \uc784\uc2dc \uc800\uc7a5 \uc704\uce58
fileupload.temp.subpath = /temp
# \ubb3b\uace0\ub2f5\ud558\uae30 \uac8c\uc2dc\ud310 \uc11c\ube0c\uc704\uce58 : \ucca8\ubd80\ud30c\uc77c \ucd5c\uc0c1\uc704 \uc704\uce58 \uc774\ud558\uc758 \uc704\uce58 \uc815\ubcf4\ub97c \uc124\uc815
fileupload.bbs.qna.subpath = /qna
#----------------------------------------
# \uc774\uba54\uc77c \ud15c\ud50c\ub9bf \uacbd\ub85c
#----------------------------------------