[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기3 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++
* 초기 세팅 (개발환경) 및 설정
https://rockbottomdevbus.blogspot.com/2024/04/springboot-1-325-intellij-idea-20241.html
* 이전글
[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기1 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++
https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-1-login-325.html
[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기2 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++
https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-2-login-325.html
* 라이브러리 추가
- build.gradle 수정
-- build.gradle
// ThymeLeaf에서 스프링 시큐리티를 사용하기 위한 라이브러리
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.2.RELEASE'
1. register.html 수정 ( authentication 관련 설정 : 권한 등을 확인 할 수 있는 객체 )
1. register.html 파일 생성 >> 경로 : resources/templates/board/register.html
-- register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://thymeleaf.org/thymeleaf-extras-springsecurity6"
layout:decorate="~{layout/basic.html}">
<head>
<title>Board Register</title>
</head>
<div layout:fragment="content">
<div class="row mt-3">
<div class="col">
<div class="card">
<div class="card-header">
Board Register
</div>
<div class="card-body">
<form action="/board/register" method="post">
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" placeholder="Title">
</div>
<div class="input-group mb-3">
<span class="input-group-text">Content</span>
<textarea class="form-control col-sm-5" rows="5" name="content"></textarea>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" placeholder="Writer"
th:value="${#authentication.principal.username}" readonly>
</div>
<div class="my-4">
<div class="float-end">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-secondary">Reset</button>
</div>
</div>
</form>
</div> <!-- end card-body -->
</div> <!--end card -->
</div> <!-- end col -->
</div> <!--end row -->
</div>
<script layout:fragment="script" th:inline="javascript">
const auth = [[${#authentication.principal}]]
console.log([[${#authentication}]]);
const errors = [[${errors}]]
console.log(errors)
let errorMsg = ''
if(errors) {
for(let i = 0 ; i < errors.length; i++ ) {
errorMsg += `${errors[i].field}으(는) ${errors[i].code} \n`
}
alert(errorMsg)
}
</script>
</html>
2. 크롬 개발자도구 콘솔에서 authentication 객체 값 확인
3. BoardController.java 인증된 사용자만 접근하게 Mapping 변경 (read/modify)
1. BoardController.java 파일 메서드에 인증추가 >> 경로 : /main/java/{pakage name}/controller
-- BoardController.java
@PreAuthorize("isAuthenticated()") // 인증된 사용자만 접근 가능...
@GetMapping({"/read","/modify"}) //조회
public void read(Long bno, PageRequestDTO pageRequestDTO, Model model) {
BoardDTO boardDTO = boardService.readOne(bno);
log.info(boardDTO);
model.addAttribute("dto", boardDTO);
}
4. 화면 테스트
http://localhost:8090/board/read?bno=197&page=1&size=10&continue
5. read.html 수정 ( authentication 관련 수정 )
1. read.html 파일 생성 >> 경로 : resources/templates/board/read.html
--read.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/basic.html}">
<head>
<title>Board Read</title>
</head>
<div layout:fragment="content">
<div class="row mt-3">
<div class="col">
<div class="card">
<div class="card-header">
Board Read
</div>
<div class="card-body">
<div class="input-group mb-3">
<span class="input-group-text">Bno</span>
<input type="text" class="form-control" th:value="${dto.bno}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" class="form-control" th:value="${dto.title}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Content</span>
<textarea class="form-control col-sm-5" rows="5" readonly>[[${dto.content}]]</textarea>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" class="form-control" th:value="${dto.writer}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">RegDate</span>
<input type="text" class="form-control" th:value="${#temporals.format(dto.regDate,'yyyy-MM-dd HH:mm:ss')}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">ModDate</span>
<input type="text" class="form-control" th:value="${#temporals.format(dto.modDate,'yyyy-MM-dd HH:mm:ss')}" readonly>
</div>
<div class="my-4" th:with="user=${#authentication.principal}">
<div class="float-end" th:with="link = ${pageRequestDTO.getLink()}">
<a th:href="|@{/board/list}?${link}|" class="text-decoration-none">
<button type="button" class="btn btn-primary ">List</button>
</a>
<a th:if="${user != null && user.username == dto.writer}" th:href="|@{/board/modify(bno=${dto.bno})}&${link}|" class="text-decoration-none">
<button type="button" class="btn btn-secondary ">Modify</button>
</a>
</div>
</div>
</div> <!--end card-body -->
</div><!--end card -->
</div> <!--end col -->
</div><!--end row -->
<!-- 댓글 처리 영역 만들기... -->
<div class="row mt-3">
<div class="col-md-12">
<div class="my-4">
<button class="btn btn-info addReplyBtn">ADD REPLY</button>
</div>
<ul class="list-group replyList">
</ul>
</div>
</div>
<div class="row mt-3">
<div class="col">
<ul class="pagination replyPaging">
</ul>
</div>
</div>
<!-- Modal 창 - registerModal -->
<div class="modal registerModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Register Reply</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="input-group mb-3">
<span class="input-group-text">Reply Text</span>
<input type="text" class="form-control replyText">
</div>
<div class="input-group mb-3" th:with="user=${#authentication.principal}">
<span class="input-group-text">Replyer</span>
<input type="text" class="form-control replyer" th:value="${user.username}" readonly>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary registerBtn">Register</button>
<button type="button" class="btn btn-outline-dark closeRegisterBtn">Close</button>
</div>
</div>
</div>
</div> <!-- end modal -->
<!-- Modal 창 - modifyModal -->
<div class="modal modifyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title replyHeader"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="input-group mb-3">
<span class="input-group-text">Reply Text</span>
<input type="text" class="form-control modifyText" >
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info modifyBtn">Modify</button>
<button type="button" class="btn btn-danger removeBtn">Remove</button>
<button type="button" class="btn btn-outline-dark closeModifyBtn">Close</button>
</div>
</div>
</div>
</div> <!-- end modify modal -->
<!-- reply를 처리하기 위한 javascript 라이브러리 불러오기... -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="/js/reply.js"></script>
</div>
<script layout:fragment="script" th:inline="javascript">
const currentUser = [[${#authentication.principal.username}]]
// // console.log(get1(bno))
// get1(bno).then(data => { //정상적 값
// console.log(data)
// }).catch(e =>{ // 실패,에러...
// console.error(e)
// })
// 댓글 목록 처리...
const bno = [[${dto.bno}]]
const replyList = document.querySelector('.replyList') // 댓글 목록 DOM
const replyPaging = document.querySelector('.replyPaging') // 댓글 페이지 목록 DOM
function printList(dtoList) {
let str = '';
if(dtoList && dtoList.length > 0) {
for (const dto of dtoList) {
str +=`<li class="list-group-item d-flex replyItem">
<span class="col-2">${dto.rno}</span>
<span class="col-6" data-rno="${dto.rno}">${dto.replyText}</span>
<span class="col-2">${dto.replyer}</span>
<span class="col-2">${dto.regDate}</span>
</li>`
}
}
replyList.innerHTML = str
}
function printPages(data) { //댓글 페이지 목록 출력
//pagination
let pageStr = '';
//이전 버튼
if(data.prev) {
pageStr += `<li class="page-item">
<a class="page-link" data-page="${data.start - 1}">PREV</a>
</li>`
}
//페이지 번호...
for (let i = data.start; i <= data.end; i++ ){
pageStr += `<li class="page-item ${i == data.page? "active":""}">
<a class="page-link" data-page="${i}">${i}</a>
</li>`
}
// 다음 버튼
if(data.next) {
pageStr += `<li class="page-item">
<a class="page-link" data-page="${data.end + 1}">NEXT</a>
</li>`
}
replyPaging.innerHTML = pageStr
}
function printReplies(page, size, goLast) {
// console.log("printReplies : "+bno, page, size)
getList({bno, page, size, goLast}).then(
data => {
// console.log("total : "+data.total)
printList(data.dtoList) //목록 처리
printPages(data) //페이지 처리
// console.log(data)
}
).catch(e => {
console.log(e)
})
}
//함수 호출
printReplies(2,10, true)
// 댓글 등록 모달
const registerModal = new bootstrap.Modal(document.querySelector(".registerModal"))
//registerModal
const registerBtn = document.querySelector(".registerBtn")
const replyText = document.querySelector(".replyText")
const replyer = document.querySelector(".replyer")
const closeRegisterBtn = document.querySelector(".closeRegisterBtn")
// ADD REPLY 버튼클릭시 모달 창 띄우고 닫기
document.querySelector(".addReplyBtn").addEventListener("click", function (e) {
registerModal.show()
}, false)
closeRegisterBtn.addEventListener("click", function (e) {
registerModal.hide()
}, false)
// 등록 처리 버튼 registerBtn
registerBtn.addEventListener("click", function (e) {
const replyObj = {
bno: bno,
replyText: replyText.value,
replyer: replyer.value
}
addReply(replyObj).then(result => {
alert(result.rno)
registerModal.hide()
replyText.value = ''
replyer.value = ''
printReplies(1, 10, true) // 댓글 목록 갱신
}).catch(e => {
alert("Exception....")
})
}, false)
// 페이지 번호 클릭시 이동 처리...
let page = 1
let size = 10
replyPaging.addEventListener("click", function (e) {
e.preventDefault()
e.stopPropagation()
const target = e.target
if(!target || target.tagName != 'A') {
return
}
const pageNum = target.getAttribute("data-page")
page = pageNum
printReplies(page, size)
}, false)
//modifyModal
const modifyModal = new bootstrap.Modal(document.querySelector(".modifyModal"))
const replyHeader = document.querySelector(".replyHeader")
const modifyText = document.querySelector(".modifyText")
const modifyBtn = document.querySelector(".modifyBtn")
const removeBtn = document.querySelector(".removeBtn")
const closeModifyBtn = document.querySelector(".closeModifyBtn")
// 수정을 위한 replyList에 이벤트 처리 추가...
replyList.addEventListener("click", function (e) {
e.preventDefault()
e.stopPropagation()
const target = e.target
if(!target || target.tagName != 'SPAN') {
return
}
const rno = target.getAttribute("data-rno")
if(!rno) {
return
}
getReply(rno).then(reply => {
console.log(reply)
replyHeader.innerHTML = reply.rno
modifyText.value = reply.replyText
modifyModal.show()
hasAuth = currentUser === reply.replyer // 댓글의 작성자와 현자 사용자가 일치하는지 일치여부를 확인
}).catch(e => alert('error'))
}, false)
modifyBtn.addEventListener("click",function (e) {
if(!hasAuth){
alert("댓글 작성자만 수정 가능합니다.")
modifyModal.hide()
return
}
const replyObj = {
bno:bno,
rno:replyHeader.innerHTML,
replyText:modifyText.value
}
modifyReply(replyObj).then(result => {
alert(result.rno + ' 댓글이 수정되었습니다.')
replyText.value = ''
modifyBtn.hide()
printReplies(page,size)
}).catch(e => console.log(e))
closeModifyBtn.addEventListener("click",function (e) {
modifyModal.hide()
},false)
removeBtn.addEventListener("click",function (e) {
if(!hasAuth){
alert("댓글 작성자만 삭제 가능합니다.")
modifyModal.hide()
return
}
removeReply(replyHeader.innerHTML).then(result=>{
alert(result.rno + `댓글이 삭제되었습니다.`)
replyText.value = ''
modifyModal.hide()
page = 1 // 1번 페이지로 이동, 이설정이 없으면 이전페이지로
printReplies(page,size)
}).catch(e => {console.log(e)})
})
},false)
</script>
</html>
6. 화면 동작 테스트 (작성한 작성자가 수정버튼이 보이게끔 // 댓글의 작성자와 현자 사용자가 일치하는지 일치여부를 확인 // form으로 action할때 값 username넣어주기 )
http://localhost:8090/board/read?bno=197&page=1&size=10&continue
7. BoardController.java 수정 : 게시물 작성자와 로그인한 사용자가 같은경우 수정 처리
1. BoardController.java 파일 메서드에 인증추가 >> 경로 : /main/java/{pakage name}/controller
-- BoardController.java
@PreAuthorize("principal.username==#boardDTO.writer") // 수정 처리는 게시물 작성자와 로그인한 사용자가 같은경우
@PostMapping("/modify")
public String modify(PageRequestDTO pageRequestDTO,
@Valid BoardDTO boardDTO,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
log.info("board modify post ...... " + boardDTO);
if(bindingResult.hasErrors()) {
log.info("has errors......");
String link = pageRequestDTO.getLink();
redirectAttributes.addFlashAttribute("errors",bindingResult.getAllErrors());
redirectAttributes.addAttribute("bno", boardDTO.getBno());
return "redirect:/board/modify?"+link;
}
boardService.modify(boardDTO);
redirectAttributes.addFlashAttribute("result","modified");
redirectAttributes.addAttribute("bno", boardDTO.getBno());
return "redirect:/board/read";
}
@PreAuthorize("principal.username==#boardDTO.writer")
@PostMapping("/remove")
public String remove(BoardDTO boardDTO, RedirectAttributes redirectAttributes) {
log.info("remove post.... "+ boardDTO.getBno());
boardService.remove(boardDTO.getBno());
redirectAttributes.addFlashAttribute("result","removed");
return "redirect:/board/list";
}
8. 화면 동작 테스트 ( 작성자에 주의하여 수정 / 삭제 테스트 )
http://localhost:8090/board/read?bno=197&page=1&size=10&continue
* 다음글
[SpringBoot] 스프링부트 프로젝트 생성 후 spring-security 사용 및 테스트하기4 login 설정~로그인 설정~(세팅 스프링 시큐리티 세션 스프링부트3.25 )_IntelliJ IDEA 2024.1JDK17 spring-boot test session http--++
https://rockbottomdevbus.blogspot.com/2024/05/springboot-spring-security-4-login-325.html
댓글
댓글 쓰기