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> <artifactId>commons-lang3</artifactId>
<version>3.3.2</version> <version>3.3.2</version>
</dependency> </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> </dependencies>

View File

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

View File

@ -108,6 +108,9 @@ fileupload.base.path = /nfs_nas_dev/uac/nlib
# \uc6b4\uc601\uc11c\ubc84 # \uc6b4\uc601\uc11c\ubc84
#fileupload.base.path = /nfs_nas/uac/nlib #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) # \ucd5c\ub300 \ud30c\uc77c \ud06c\uae30 (MB)
fileupload.max.mbsize = 10 fileupload.max.mbsize = 10
# \ucd5c\ub300 \ud30c\uc77c \uac1c\uc218 # \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 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 # \uc774\uba54\uc77c \ud15c\ud50c\ub9bf \uacbd\ub85c
#---------------------------------------- #----------------------------------------