웹사이트에 script가 적용되서 내용이 다 날라가버림 - 해킹의 일종에 script가 적용되서 내용이 다 날라가버림 - 해킹의 일종
태그를 무력화시키고 문자그대로 실행되게 해야함
<img alt="첨부파일" src="<%=request.getContextPath() %>/images/file.png" width=16px>
<a href="<%= request.getContextPath() %>/board/fileDownload?no=<%= board.getNo() %>"><%= board.getAttach().getOriginalFileName() %></a>
<% } %>
package board.controller;
@WebServlet("/board/fileDownload")
public class FileDownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
BoardService boardService = new BoardService();
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 사용자 입력값 : no 게시글 번호
int no = Integer.parseInt(request.getParameter("no"));
//2. 업무로직 : 첨부파일 조회
//originalFileName, renamedFileName
Attachment attach = boardService.selectBoardByNo(no).getAttach();
System.out.println("attach@servlet = " + attach);
//3. 파일 다운로드
//a. 입출력스트림 생성
String saveDirectory = getServletContext().getRealPath("/upload/board");
//new File(저장될 경로, 저장될 파일명)
File f = new File(saveDirectory, attach.getRenamedFileName());
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream(f));
//대상이 응답메세지인 출력 스트림 가져오기
BufferedOutputStream bos =
new BufferedOutputStream(response.getOutputStream());
//b. 응답헤더작성
//파일명 한글로 인코딩 처리
//ISO-8859-1(톰캣용)으로 처리해서 헤더에 넣으면, 다운받을 때 톰캣이 다시 인코딩을 해준다
String responseFileName = new String(attach.getOriginalFileName().getBytes("utf-8"), "ISO-8859-1");
//서버단에선 깨져서 나온다
System.out.println("responseFileName = " + responseFileName);
response.setContentType("application/octet-stream; charset=utf-8"); //2진데이터
response.setHeader("Content-Disposition", "attachment;filename=" + responseFileName);
//c. 파일출력
int read = -1;
while((read = bis.read()) != -1) {
bos.write(read);
}
//d. 자원반납
bos.close();
bis.close();
}
}
FileDownloadServlet.java로 이동해서 작성
한글이 깨지지 않도록 작성해주기
<%@page import="board.model.vo.Board"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/common/header.jsp" %>
**<%
Board board = (Board)request.getAttribute("board");
boolean editable =
loginMember != null &&
(loginMember.getMemberId().equals(board.getWriter())
|| MemberService.ADMIN_ROLE
.equals(loginMember.getMemberRole()));
%>**
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/board.css" />
<section id="board-container">
<h2>게시판</h2>
<table id="tbl-board-view">
//중략
**<% if(editable){ %>
<%-- 작성자와 관리자만 마지막행 수정/삭제버튼이 보일수 있게 할 것 --%>
<th colspan="2">
<input type="button" value="수정하기" onclick="updateBoard()">
<input type="button" value="삭제하기" onclick="deleteBoard()">
</th>
<% } %>**
</tr>
</table>
</section>
<form
action="<%= request.getContextPath() %>/board/boardDelete"
name="boardDelFrm"
method="POST">
<input type="hidden" name="no" value="<%= board.getNo() %>" />
</form>
<script>
**function deleteBoard(){
if(confirm("게시글을 정말 삭제하시겠습니까?")){
$(document.boardDelFrm).submit(); //확인 시, 히든폼 제출
}
}**
</script>
<%@ include file="/WEB-INF/views/common/footer.jsp" %>
boardView.jsp으로 이동해서 작성로 이동해서 작성
on delete cascade속성이라 게시글만 지우면 첨부파일 행도 알아서 지워짐
public int deleteBoard(int no) {
Connection conn = getConnection();
int result = 0;
try {
result = boardDao.deleteBoard(conn, no);
if(result == 0)
throw new IllegalArgumentException("해당 게시글이 존재하지 않습니다. : " + no);
commit(conn);
} catch(Exception e) {
e.printStackTrace(); //controller에서 예외메세지 표시
rollback(conn);
throw new BoardException("게시물 삭제 오류", e); // controller가 예외처리를 결정할 수 있도록 넘김
} finally {
close(conn);
}
return result;
}
BoardService.java로 이동해서 작성
service에서 트랜잭션처리 할 때 try catch처리
운영 중에 서버의 오류페이지를 노출하면 안됨
에러코드는 절대 유출되면 안됨. 보안사고를 유발할 수 있음
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
int no = Integer.parseInt(request.getParameter("no"));
System.out.println("no@servlet = " + no);
int result = boardService.deleteBoard(no);
HttpSession session = request.getSession();
System.out.println("result@deleteServlet = " + result);
if(result > 0) {
session.setAttribute("msg", "게시물이 삭제되었습니다");
//boardList로 redirection
response.sendRedirect(request.getContextPath() + "/board/boardList");
}
else {
session.setAttribute("msg", "게시물 삭제에 실패했습니다.");
}
}catch(Exception e) {
//예외 로깅을 컨트롤러에서 한다
e.printStackTrace();
//예외페이지 전환을 위해서 was에 예외 다시 던지기
throw e;
}
}
BoardDeleteServlet.java로 이동해서 작성
try/catch절로 감싸고 톰캣에 에러를 던진다.
커스텀 예외페이지 jsp 생성
페이지 지시어에 isErrorPage속성을 true로 지정하면, 던져진 예외객체에 exception 키워드로 선언없이 접근 가능
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isErrorPage="true"%>
<%--
isErrorPage속성을 true로 지정하면,
던져진 예외객체에 exception 키워드로 선언없이 접근 가능
--%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>error</title>
</head>
<body style="text-align: center;">
<h1>이용에 불편을 드려 죄송합니다.</h1>
<p style="color: red;"><%= exception.getMessage() %></p>
//servlce 또는 dao에서 던진 에러메세지가 커스텀 에러 페이지를 표시
<p><a href="<%= request.getContextPath() %>">메인페이지로 돌아가기</a></p>
</body>
</html>
web.xml로 이동해서
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/views/common/error.jsp</location>
</error-page>
추가해주기
예외처리시 브라우져에 출력되는 화면
service와 dao에서는 에러를 던져서 (throw new BoardException() : RuntimeException 을 상속받은 클래스)컨트롤러에서 반드시 한번은 에러를 잡아야 한다
컨트롤러에서 에러를 잡으면
예외를 받은 was는 tomcat-error.jsp나 custom-error.jsp중에 골라서 표시한다
Controller에서는 try catch처리를 꼭 해야한다.
//선략
<% if(editable){ %>
<%-- 작성자와 관리자만 마지막행 수정/삭제버튼이 보일수 있게 할 것 --%>
<th colspan="2">
<input type="button" value="수정하기" onclick="updateBoard()">
<input type="button" value="삭제하기" onclick="deleteBoard()">
</th>
<% } %>
</tr>
//중략
</form>
<script>
function updateBoard(){
location.href="<%= request.getContextPath() %>/board/boardUpdate?no=<%= board.getNo() %>";
}
//후략
boardView.jsp이동해서 작성
public class BoardUpdateServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private BoardService boardService = new BoardService();
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.사용자 입력값
int no = Integer.parseInt(request.getParameter("no"));
//2.업무로직
Board board = boardService.selectOne(no);
//3.jsp포워딩
request.setAttribute("board", board);
request.getRequestDispatcher("/WEB-INF/views/board/boardUpdateForm.jsp")
.forward(request, response);
}
BoardUpdateServlet.java이동해서 doGet작성
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. MultipartRequest객체 생성
// /WebContent/upload/board/업로드파일명.jpg
// web rool dir를 절대경로로 반환
String saveDirectory = getServletContext().getRealPath("/upload/board");
System.out.println("saveDirectory@servlet = " + saveDirectory);
//최대파일허용크기 10mb = 10 * 1kb * 1kb
int maxPostSize = 10 * 1024 * 1024;
//인코딩
String encoding = "utf-8";
//파일명 변경정책 객체
//중복파일인 경우, numbering처리
//filerename : 20210406191919_123.jpg
//FileRenamePolicy policy = new DefaultFileRenamePolicy();
FileRenamePolicy policy = new MvcFileRenamePolicy();
MultipartRequest multipartRequest =
new MultipartRequest(
request,
saveDirectory,
maxPostSize,
encoding,
policy
);
//2. db에 게시글/첨부파일 정보 저장
//2-1. 사용자 입력값처리
int no = Integer.parseInt(multipartRequest.getParameter("no"));
String title = multipartRequest.getParameter("title");
String writer = multipartRequest.getParameter("writer");
String content = multipartRequest.getParameter("content");
//업로드한 파일명
String originalFileName = multipartRequest.getOriginalFileName("upFile");
String renamedFileName = multipartRequest.getFilesystemName("upFile");
//Board board = new Board(0, title, writer, content, null, 0, null);
Board board = new Board();
board.setNo(no);
board.setTitle(title);
board.setWriter(writer);
board.setContent(content);
//첨부파일이 있는 경우
//multipartRequest.getFile("upFile"):File != null
if(originalFileName != null) {
Attachment attach = new Attachment();
attach.setBoardNo(no);
attach.setOriginalFileName(originalFileName);
attach.setRenamedFileName(renamedFileName);
board.setAttach(attach);
}
//2. 업무로직 : db에 insert
int result = boardService.updateBoard(board);
String msg = (result > 0) ? "게시글 수정 성공!" : "게시글 수정 실패!";
String location = request.getContextPath()
+ "/board/boardView?no=" + board.getNo();
//3. DML요청 : 리다이렉트 & 사용자피드백
// /mvc/board/boardList
HttpSession session = request.getSession();
session.setAttribute("msg", msg);
response.sendRedirect(location);
} catch (Exception e) {
e.printStackTrace();
throw e; // was한테 다시 던져서 에러페이지로 전환함.
}
}
BoardUpdateServlet.java이동해서 doPost작성
BoardEnrollServlet.java의 doPost코드를 가져온 후 board객체에 parameter에서 받은 no값 추가
//선략
<%
Board board = (Board)request.getAttribute("board");
%>
<section id="board-container">
<h2>게시판 수정</h2>
<form
name="boardUpdateFrm"
action="<%=request.getContextPath() %>/board/boardUpdate"
enctype="multipart/form-data"
method="post">
<input type="hidden" name="no" value="<%= board.getNo() %>" />
<table id="tbl-board-view">
<tr>
<th>제 목</th>
<td><input type="text" name="title" value="<%= board.getTitle() %>" required></td>
</tr>
<tr>
<th>작성자</th>
<td>
<input type="text" name="writer" value="<%= board.getWriter() %>" readonly/>
</td>
</tr>
<tr>
<th>첨부파일</th>
<td >
<%-- input:file의 value속성은 보안성을 이유로 임의변경 할 수 없다. --%>
<% if(board.getAttach() != null){ %>
<p style ="margin:0">
<img src="<%= request.getContextPath() %>/images/file.png" width="16px"/>
<%= board.getAttach().getOriginalFileName() %>
</p>
<% } %>
</td>
</tr>
<tr>
<th>내 용</th>
<td><textarea rows="5" cols="50" name="content"><%= board.getContent() %></textarea></td>
</tr>
<tr>
//후략
BoardUpdateForm.jsp으로 이동해서 작성
board-query.properties에
updateBoard = update board set title = ?, content = ? where no = ?
추가
public int updateBoard(Board board) {
Connection conn = getConnection();
int result = 0;
try {
//1. board update : board테이블에 게시글이 있는 상황
result = boardDao.updateBoard(conn, board);
//2. attachment insert : attachment테이블에 data가 없는 상황
if(board.getAttach() != null) {
result = boardDao.insertAttachment(conn, board.getAttach());
}
commit(conn);
} catch(Exception e) {
rollback(conn);
throw e;
}finally {
close(conn);
}
return result;
}
BoardDao.java으로 이동해서 작성 : 예외 던지기 전에 롤백처리
//선략
<script>
$("[name=upFile]").change(function(){
console.log($(this).val());
if($(this).val() != ""){
//파일 선택
$("#delFile")
.prop("checked", true)
.on('click', function(){
return false; //한번 체크하면 체크 풀 수 없음
})
}
else{
//파일 선택 취소 (파일선택창에서 열기 안하고 취소 눌렀을 때)
$("#delFile")
.prop("checked", false)
.off('click'); //return false처리했던 이벤트 헨들러 제거
}
});
//후략
BoardUpdateForm.jsp으로 이동해서 작성
첨부파일 업데이트
기존 첨부파일 무력화
status컬럼의 데이터를 Y로 변경