새싹개발자 2020. 12. 13. 01:06

<실습>

1. board_list.jsp 완료

- boardVO.java 생성, SecurityCode.java 생성, AdminController에 board_list 바인드매핑 생성

2. board_view.jsp 완료

- board_view.jsp 생성, AdminController에 board_view 바인드매핑 생성

- ReplyController 생성

3. board_write.jsp 완료

- board_write.jsp 생성, AdminController에 board_write 바인드매핑 생성

 

3개의 jsp파일 각각 board_list.html, board_view.html, board_write.html에서 복사해서 생성한다.

전체적으로 경로 확인, "/admin/board/board_list(or view or write)" 로 수정

 

1. board_list.jsp 

- boardVO 생성 -> AdminController에서 model클래스를 이용해서 jsp로 board_list 데이터 세트를 보낼 때 필요한 클래스

package org.edu.vo;

import java.util.Date;

/**
 * 게시판에서 사용되는 데이터 입출력 클래스
 * @author 이시은
 *
 */
public class BoardVO {
	//멤버변수 선언
	private Integer bno; //int는 입력값이 null일 때 에러나기 때문에, Integer로 변경
	private String title;
	private String content;
	private String writer;
	private Date regdate;
	private Date update_date;
	private Integer view_count;
	private Integer reply_count;

	@Override
	public String toString() {
		return "디버그 BoardVO [bno=" + bno + ", title=" + title + ", content=" + content + ", writer=" + writer + ", regdate="
				+ regdate + ", update_date=" + update_date + ", view_count=" + view_count + ", reply_count="
				+ reply_count + "]";
	}

	public Integer getBno() {
		return bno;
	}
	public void setBno(Integer bno) {
		this.bno = bno;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public Date getRegdate() {
		return regdate;
	}
	public void setRegdate(Date regdate) {
		this.regdate = regdate;
	}
	public Date getUpdate_date() {
		return update_date;
	}
	public void setUpdate_date(Date update_date) {
		this.update_date = update_date;
	}
	public Integer getView_count() {
		return view_count;
	}
	public void setView_count(Integer view_count) {
		this.view_count = view_count;
	}
	public Integer getReply_count() {
		return reply_count;
	}
	public void setReply_count(Integer reply_count) {
		this.reply_count = reply_count;
	}

}

 

- SecurityCode.java 생성

- 유해사이트로 이동하게 하는 등의 스크립트의 유입을 막기 위해 시큐어코딩을 해준다.

- servlet-context.xml<context:component-scan base-package="org.edu.util" /> 추가

* servlet-context 안에 있는 component-scan이 지정한 package경로에 존재하는 @Controller, @Service, @Repository, @RestController(Rest-API) 어노테이션 클래스를  자동으로  읽어들여서 빈으로 등록. => 실행가능

- AdminController에 @Inject 방식으로 가져와서 쓴다.

package org.edu.util;

import org.springframework.stereotype.Controller;

//컨트롤러 클래스를 사용하는 이유는 스프링에서 사용 가능한 bean으로 만들기 위해서.
@Controller
public class SecurityCode {
	/**
	 * XSS 방지 처리. Cross Site Script 약자 XSS
	 * 
	 * @param data
	 * @return
	 */
	public String unscript(String data) {
        if (data == null || data.trim().equals("")) {
            return "";
        }
        String ret = data;
        ret = ret.replaceAll("<(S|s)(C|c)(R|r)(I|i)(P|p)(T|t)", "&lt;script");
        ret = ret.replaceAll("</(S|s)(C|c)(R|r)(I|i)(P|p)(T|t)", "&lt;/script");
        ret = ret.replaceAll("<(O|o)(B|b)(J|j)(E|e)(C|c)(T|t)", "&lt;object");
        ret = ret.replaceAll("</(O|o)(B|b)(J|j)(E|e)(C|c)(T|t)", "&lt;/object");
        ret = ret.replaceAll("<(A|a)(P|p)(P|p)(L|l)(E|e)(T|t)", "&lt;applet");
        ret = ret.replaceAll("</(A|a)(P|p)(P|p)(L|l)(E|e)(T|t)", "&lt;/applet");
        ret = ret.replaceAll("<(E|e)(M|m)(B|b)(E|e)(D|d)", "&lt;embed");
        ret = ret.replaceAll("</(E|e)(M|m)(B|b)(E|e)(D|d)", "&lt;embed");
        ret = ret.replaceAll("<(F|f)(O|o)(R|r)(M|m)", "&lt;form");
        ret = ret.replaceAll("</(F|f)(O|o)(R|r)(M|m)", "&lt;form");
        return ret;
    }
}

- AdminController에 board_list 바인드 매핑 추가, 더미데이터 입력

@RequestMapping(value="/admin/board/board_list", method=RequestMethod.GET)
	public String board_list(Model model) throws Exception {
		
		//테스트용 더미 게시판 데이터 만들기
		BoardVO board_input = new BoardVO();
		board_input.setBno(1);
		board_input.setTitle("첫번째 게시물입니다.");
		board_input.setContent("첫번째 게시물 내용입니다.<br>줄바꿈했습니다.");
		board_input.setWriter("admin");
		Date regdate = new Date();
		board_input.setRegdate(regdate);
		board_input.setView_count(2);
		board_input.setReply_count(0);
		
		BoardVO[] board_array = new BoardVO[2];
		board_array[0] = board_input;
		// ----------------------------------------------
		BoardVO board_input2 = new BoardVO();
		board_input2.setBno(2);
		board_input2.setTitle("두번째 게시물입니다.");
		board_input2.setContent("두번째 게시물 내용입니다.<br>줄바꿈했습니다.");
		board_input2.setWriter("user02");
		board_input2.setRegdate(regdate);
		board_input2.setView_count(2);
		board_input2.setReply_count(0);
		// board_input.setBno(2); // 게시물번호만 2로 변경, 나머지값들은 변경없이 board_array[1]에 저장
		board_array[1] = board_input2;
	
		List<BoardVO> board_list = Arrays.asList(board_array); //배열타입을 List타입으로 변경 절차.
		model.addAttribute("board_list", board_list);
		
		return "admin/board/board_list"; 
	}

 

태그라이브러리 추가, (위)시큐어코딩, (아래)데이터형식관련태그
외부 태그라이브러리 적용 예시


2. board_view.jsp 

- AdminController에 board_view 바인드 매핑 추가, 더미데이터 입력

@RequestMapping(value="/admin/board/board_view", method=RequestMethod.GET)
	public String board_view(@RequestParam("bno") Integer bno, Model model) throws Exception {
		//jsp로 보낼 더미데이터 memberVO에 담아서 보낸다.
		// 실제로는 아래처럼 더미데이터를 만드는 것이 아닌, 쿼리스트링(질의문자열)로 받아온 bno(게시물고유번호)를 이용해서
		// DB에서  SELECT * FROM tbl_board WHERE bno=? 실행이 된 결과값을 BoardVO형으로 받아서 jsp로 보내줌
		BoardVO boardVO = new BoardVO();
		boardVO.setBno(1);
		boardVO.setTitle("첫번째 게시물입니다.");
		String xss_data="첫번째 내용입니다.<br>줄바꿈 자리입니다.<script>alert('메롱')</script>";
		boardVO.setContent(securityCode.unscript(xss_data));
		boardVO.setWriter("admin");
		Date regdate = new Date();
		boardVO.setRegdate(regdate);
		boardVO.setView_count(2);
		boardVO.setReply_count(0);
		model.addAttribute("boardVO", boardVO);
		return "admin/board/board_view";
	}

 

 

jsp에서 시큐어코딩 사용 예시

- ReplyController 생성

- REST-API 기술 사용하기 때문에 따로 controller 만들어준다.

package org.edu.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * ReplyController.java 클래스
 * 댓글 구현 Rest-API 전용 Controller
 * 
 * @author 이시은
 *
 */
@RestController
public class ReplyController {
	//댓글입력 메소드(아래)
	@RequestMapping(value="/reply/reply_write", method=RequestMethod.POST)
	public ResponseEntity<String> reply_write(){
		ResponseEntity<String> responseEntity = new ResponseEntity<String>("OK", HttpStatus.OK);
		//ResponseEntity는 json 텍스트를 반환하는데, (전송내용:"SUCCESS", HttpSatus.OK(200))
		//(전송내용-e.getMessage()실페메세지값, 전송상태-HttpStatus.BAD_REQUEST(400))
		return responseEntity;
	}
	//기존 @Controller의 메소드 반환값인 파일위치 대신에
	//@RestController의 메소드의 반환값인 ResponseEntity는 json텍스트(body,전송상태값)로, Ajax로 호출한 jsp에 리턴보내게 된다.
}

실행 결과


3. board_write.jsp 

- AdminController에 board_write 바인드 매핑 추가

	@RequestMapping(value="/admin/board/board_write", method=RequestMethod.GET) //url경로
	public String board_write () throws Exception {
		return "admin/board/board_write";//파일경로
	}
	@RequestMapping(value="/admin/board/board_write", method=RequestMethod.POST)
	public String board_write(MultipartFile file, BoardVO boardVO) throws Exception {
		//POST로 받은 boardVO내용을 DB서비스에 입력하면 된다.
		//DB에 입력 후 새로고침 명령으로 게시물테러를 당하지 않으려면 redirect로 이동 처리한다.(아래)
		return "redirect:/admin/board/board_list";
	}

- 첨부 파일 설정 servlet-context.xml 추가

<!-- html폼에서 첨부파일 업로드 설정 10메가 제한 -->
<beans:bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <beans:property name="maxUploadSize" value="10485760"></beans:property>
</beans:bean>
<!-- 업로드한 파일이 저장되는 위치: 로컬PC용 --> 
<beans:bean id="uploadPath" class="java.lang.String">
  <beans:constructor-arg value="C:\\egov\\workspace\\upload" />
</beans:bean>