[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

댓글

T O P