2019. 10. 11. 19:02ㆍProject B (SPMS)/Project B 파트7
게시글의 수정 및 삭제
게시글의 수정 및 삭제는 브라우저에서는 로그인한 사용자만 접근할 수 있지만, 사용자가 URL을 조작해서도 접근이 가능하기 때문에 화면과 POST 방식으로 처리되는 부분에서 CSRF 토큰과 스프링 시큐리티를 적용한다.
게시글의 수정과 삭제에서 신경쓰이는 부분은 게시글의 수정과 삭제는 현재 로그인한 사용자와 게시글의 작성자가 동일한 경우에만 할 수 있다는 것이다.
이 처리를 과거에는 인터셉터로 처리했지만, @PreAuthorize의 경우에는 표현식으로 처리 가능하다.
views/board
modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>
<%@ include file="../includes/header.jsp"%>
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">게시판 수정</h1>
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">게시글 수정 페이지</div>
<!-- /.panel-heading -->
<div class="panel-body">
<form role="form" action="/board/modify" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token }">
<!-- 히든 파라미터 기능 시작 -->
<input type="hidden" name="pageNum" value='<c:out value="${cri.pageNum}"/>'>
<input type="hidden" name="amount" value='<c:out value="${cri.amount}"/>'>
<!-- 히든 파라미터 기능 종료 -->
<!-- Criteria의 키워드와 타입에 대한 처리 시작 -->
<input type='hidden' name='keyword' value='<c:out value="${cri.keyword}"/>'>
<input type='hidden' name='type' value='<c:out value="${cri.type}"/>'>
<!-- Criteria의 키워드와 타입에 대한 처리 종료 -->
<div class="form-group">
<label>글 번호</label>
<input class="form-control" name="bno" value='<c:out value="${board.bno }"/>' readonly="readonly">
</div>
<div class="form-group">
<label>등록일자</label>
<input class="form-control" name='regDate'
value='<fmt:formatDate pattern = "yyyy/MM/dd" value = "${board.regdate}" />' readonly="readonly">
</div>
<div class="form-group">
<label>수정일자</label>
<input class="form-control" name='updateDate'
value='<fmt:formatDate pattern = "yyyy/MM/dd" value = "${board.updateDate}" />' readonly="readonly">
</div>
<div class="form-group">
<label>작성자</label>
<input class="form-control" name="writer" value='<c:out value="${board.writer }"/>' readonly="readonly">
</div>
<div class="form-group">
<label>제목</label>
<input class="form-control" name="title" value='<c:out value="${board.title }"/>'>
</div>
<div class="form-group">
<label>내용</label>
<textarea class="form-control" rows="10" name="content"><c:out value="${board.content}"/></textarea>
</div>
<sec:authentication property="principal" var="pinfo"/>
<sec:authorize access="isAuthenticated()">
<c:if test="${pinfo.username eq board.writer }">
<button type="submit" data-oper='modify' class="btn btn-default">수정</button>
<button type="submit" data-oper='remove' class="btn btn-danger">삭제</button>
</c:if>
</sec:authorize>
<button type="submit" data-oper='list' class="btn btn-info">목록</button>
</form>
</div>
<!-- /.panel-body -->
</div>
<!-- /.panel -->
</div>
<!-- /.col-lg-12 -->
</div>
<!-- /.row -->
</div>
<script type="text/javascript">
$().ready(function() {
var formObj = $("form");
$('button').on("click", function(e){
e.preventDefault();
var operation = $(this).data("oper");
console.log(operation);
if(operation === 'remove'){
formObj.attr("action", "/board/remove");
}else if(operation === 'list'){
//move to list
formObj.attr("action", "/board/list").attr("method","get");
var pageNumTag = $("input[name='pageNum']").clone();
var amountTag = $("input[name='amount']").clone();
// 키워드와 타입 추가
var keywordTag = $("input[name='keyword']").clone();
var typeTag = $("input[name='type']").clone();
formObj.empty();
formObj.append(pageNumTag);
formObj.append(amountTag);
formObj.append(keywordTag);
formObj.append(typeTag);
}
formObj.submit();
});
});
</script>
<!-- /#page-wrapper -->
<%@ include file="../includes/footer.jsp"%>
우선 상단에 스프링 시큐리티 태그 라이브러리를 쓸 수 있게 설정하고, POST 방식으로 처리되는 부분이므로 CSRF 토큰을 추가한다.
조회와 마찬가지로 현재 로그인한 사용자가 게시글의 작성자인 경우에만 수정과 삭제가 가능하도록 제어한다.
src/main/java
com.spms.controller
BoardController.java
package com.spms.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.spms.domain.BoardVO;
import com.spms.domain.Criteria;
import com.spms.domain.PageDTO;
import com.spms.service.BoardService;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;
@Controller
@Log4j
@RequestMapping("/board/*")
@AllArgsConstructor
public class BoardController {
private BoardService boardService;
@GetMapping("/list")
public void list(Criteria cri, Model model) {
log.info("list" + cri);
boardService.getList(cri).forEach(board -> log.info(board));
model.addAttribute("list", boardService.getList(cri));
int total = boardService.getTotal(cri);
log.info("total : " + total);
model.addAttribute("pageMaker", new PageDTO(cri, total));
}
@GetMapping("/register")
@PreAuthorize("isAuthenticated()")
public void register() {
}
@PostMapping("/register")
@PreAuthorize("isAuthenticated()")
public String register(BoardVO board, RedirectAttributes rttr) {
log.info("register : " + board);
boardService.register(board);
rttr.addFlashAttribute("result", board.getBno());
return "redirect:/board/list";
}
@GetMapping({ "/get", "/modify" })
public void get(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, Model model) {
log.info("/get or modify");
model.addAttribute("board", boardService.get(bno));
}
@PostMapping("/modify")
@PreAuthorize("principal.username == #board.writer")
public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
log.info("modify:" + board);
if (boardService.modify(board)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
@PostMapping("/remove")
@PreAuthorize("principal.username == #writer")
public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr, String writer) {
log.info("remove..." + bno);
if (boardService.remove(bno)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list" + cri.getListLink();
}
}
BoardController에서는 메서드를 실행하기 전에 로그인한 사용자와 현재 파라미터로 전달되는 작성자가 일치하는지 체크한다.
@PreAuthorize의 경우 문자열로 표현식을 지정할 수 있는데, 이 때 컨트롤러에 전달되는 파라미터를 같이 쓸 수 있으므로 유용하다.
삭제의 경우 기존에는 파라미터로 게시글의 번호 bno만을 받았지만, 작성자를 의미하는 writer를 같이 추가해서 @PreAuthorize 로 검사하도록 한다.
기존과 달라진 부분은 파라미터로 writer가 추가된 부분과 해당 파라미터를 @PreAuthorize에서 #writer를 이용해서 체크한 부분이다.
게시글의 수정은 파라미터로 Board 타입의 객체를 받도록 설계되어있다.
'Project B (SPMS) > Project B 파트7' 카테고리의 다른 글
[B -2-59] 스프링 시큐리티 6 (0) | 2019.10.12 |
---|---|
[B -2-58] 스프링 시큐리티 5 (0) | 2019.10.12 |
[B -2-56] 스프링 시큐리티 3 (0) | 2019.10.11 |
[B -2-55] 스프링 시큐리티 2 (0) | 2019.10.10 |
[B -2-54] 스프링 시큐리티 1 (0) | 2019.10.10 |