목차 | |
1. | security |
1. security
◈ header.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!-- th:fragment="이름" : 레이아웃에서 사용할 조각 -->
<div th:fragment="header" class="container">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Boot</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/board/register">BoardRegister</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/board/list">BoardList</a>
</li>
<th:block sec:authorize="isAnonymous()">
<li class="nav-item">
<a class="nav-link" href="/user/signup">SignUp</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/login">Login</a>
</th:block>
<li class="nav-item" sec:authorize="hasRole('ROLE_ADMIN')">
<a class="nav-link" href="/user/list">userList</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/user/modify" th:with="auth=${#authentication.getPrincipal()}">([[${auth.userVO.email}]])modify</a>
</li>
</li><li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/user/last">Log out</a>
</li>
</ul>
</div>
</div>
</nav>
<h5>Header Area</h5>
</div>
◈ detail.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<div layout:fragment="content" class="container-md">
<h1>Boot Detail page</h1>
<hr>
<th:block th:with="bvo=${bdto.bvo}">
<form action="/board/modify" method="post" id="modForm" enctype="multipart/form-data">
<div class="mb-3">
<label for="r" class="form-label">Created At</label>
<input type="text" class="form-control" name="regDate" id="r" th:value="${bvo.regDate}" readonly>
</div><div class="mb-3">
<input type="hidden" class="form-control" name="bno" id="n" th:value="${bvo.bno}">
</div>
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" name="title" id="title" th:value="${bvo.title}" readonly>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" name="writer" id="writer" th:value="${bvo.writer}" readonly>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" name="content" id="content" rows="3" readonly>[[${bvo.content}]]</textarea>
</div>
<!-- file print line -->
<div class="mb-3">
<ul class="list-group">
<li th:each="fvo:${bdto.flist}" class="list-group-item">
<div th:if="${fvo.fileType > 0}" class="ms-2 me-auto">
<img th:src="@{|/upload/${fvo.saveDir}/${fvo.uuid}_${fvo.fileName}|}" alt="img" />
</div>
<div th:unless="${fvo.fileType > 0}" class="ms-2 me-auto">
<!-- icon -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-paperclip" viewBox="0 0 16 16">
<path d="M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0z"/>
</svg>
</div>
<div class="ms-2 me-auto">
<div class="fw-bold">[[${fvo.fileName}]]</div>
[[${fvo.regDate}]]
</div>
<span class="badge text-bg-success rounded-pill">[[${fvo.fileSize}]]Bytes</span>
<button type="button" th:data-uuid="${fvo.uuid}" class="btn btn-outline-danger bnt-nm file-x" disabled>x</button>
</li>
</ul>
</div>
<!-- file 추가 라인 -->
<div class="mb-3">
<input type="file" class="form-control" name="files" id="file" multiple style="display:none;">
</div>
<button type="button" id="trigger" class="btn btn-primary" disabled>File Upload</button> <br>
<button type="button" id="listBtn" class="btn btn-primary">List</button>
<!-- de-tail page에서 modify 상태로 변경하기 위한 버튼 -->
<button type="button" id="modBtn" class="btn btn-warning">Modify</button>
<a th:href="@{/board/delete(bno=${bvo.bno})}">
<button type="button" id="delBtn" class="btn btn-danger">Delete</button>
</a>
</form>
<!-- comment line -->
<!-- post -->
<div class="input-group mb-3" sec:authorize="isAuthenticated()">
<span class="input-group-text" id="cmtWriter" th:text="${#authentication.getPrincipal().userVO.nickName}"></span>
<input type="text" id="cmtText" class="form-control" placeholder="Add Comment..." aria-label="Username" aria-describedby="basic-addon1">
<button type="button" id="cmtAddBtn" class="btn btn-secondary">post</button>
</div>
<!-- spread -->
<ul class="list-group list-group-flush" id="cmtListArea">
<li class="list-group-item">
<div class="ms-2 me-auto">
<div class="fw-bold">writer</div>
Content
</div>
<span class="badge text-bg-primary rounded-pill">regDate</span>
</li>
</ul>
<!-- moreBtn-->
<div>
<button type="button" id="moreBtn" data-page="1" class="btn btn-dark" style="visibility:hidden">MORE + </button>
</div>
<!-- modal -->
<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="cmtWriterMod">${authNick }</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" id="cmtTextMod">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" id="cmtModBtn" class="btn btn-primary">changes</button>
</div>
</div>
</div>
</div>
<script th:inline="javascript" >
let bnoVal = [[${bvo.bno}]];
console.log(bnoVal);
let nickName = "";
console.log(nickName);
</script>
<th:block sec:authorize="isAuthenticated()" th:with="auth=${#authentication.getPrincipal().userVO}">
<script th:inline="javascript" >
nickName = [[${auth.nickName}]];
console.log(nickName);
</script>
</th:block>
</th:block>
<script th:src="@{/js/boardDetail.js}"></script>
<script th:src="@{/js/boardRegister.js}"></script>
<script th:src="@{/js/boardComment.js}"></script>
<script>
spreadCommentList(bnoVal);
</script>
</div>
◈ list.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<div layout:fragment="content" class="container-md">
<h1>User List Page</h1>
<hr>
<div class="col" th:each="uvo:${userList}">
<div class="card border-dark mb-3" style="max-width: 540px;">
<div class="row g-0">
<div class="col-md-4">
<img src="/image/boo.png" class="img-fluid rounded-start" alt="...">
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">[[${uvo.nickName}]]</h5>
<p class="card-text">[[${uvo.email}]] ([[${uvo.regDate}]]) </p>
<p class="card-text">Last Login : [[${uvo.lastLogin}]]</p>
<span class="badge rounded-pill text-bg-success" th:each="authList:${uvo.authList}">[[${authList.auth}]]</span>
</div>
</div>
</div>
</div>
</div>
</div>
◈ modify.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="container-md">
<h1>User List Page</h1>
<hr>
<div class="mt-4 p-5 rounded" th:with="auth=${#authentication.getPrincipal()}">
<div class="row">
<div class="col">
<div class="card border-dark mb-3" style="max-width: 540px;" >
<div class="row g-0">
<div class="col-md-4">
<img src="/image/boo.png" class="img-fluid rounded-start" alt="...">
</div>
<div class="col-md-8">
<form action="/user/modify" method="post">
<h5 class="card-body">
<input type="email" name="email" class="form-control" th:value="${auth.userVO.email}" readonly>
<input type="text" name="nickName" class="form-control" th:value="${auth.userVO.nickName}">
<input type="text" name="pwd" class="form-control" placeholder="Enter Password">
</h5>
<span class="badge rounded-pill text-bg-success" th:each="authList:${auth.userVO.authList}">[[${authList.auth}]]</span>
<button type="submit" class="btn btn-primary btn-sm">modify</button>
<a th:href="@{/user/remove}"><button type="button" class="btn btn-danger btn-sm">delete</button></a>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</html>
◈ boardComment.js
console.log("boardComment.js in");
console.log(bnoVal);
console.log(nickName);
document.getElementById('cmtAddBtn').addEventListener('click',()=>{
const cmtText = document.getElementById('cmtText');
if(cmtText == null || cmtText == ''){
alert('댓글 내용을 입력해주세요.')
cmtText.focus();
return false;
}
const cmtData = {
bno : bnoVal,
writer : document.getElementById('cmtWriter').innerText,
content : cmtText.value
};
postCommentToServer(cmtData).then(result => {
if(result > 0){
alert("댓글 등록 성공");
cmtText.value="";
}
// 댓글 뿌리기 호출
spreadCommentList(bnoVal);
})
});
function spreadCommentList(bnoVal, page=1){
getCommentListFromServer(bnoVal, page).then(result =>{
console.log(result);
const ul = document.getElementById('cmtListArea');
if(result.cmtList.length > 0){
if(page == 1){
ul.innerHTML=""; // 기존 샘플값 비우기.
}
for(let cvo of result.cmtList){
let li = `<li data-cno = "${cvo.cno}" class="list-group-item">`;
li += `<div class="ms-2 me-auto">`;
li += `<div class="fw-bold">${cvo.writer}</div>`;
li += `${cvo.content}`;
li += `</div>`;
li += `<span class="badge text-bg-primary rounded-pill">${cvo.regDate}</span>`;
if(nickName == cvo.writer){
li += `<button type="button" class="btn btn-outline-warning btn-sm mod" data-bs-toggle="modal" data-bs-target="#myModal">%</button>`;
li += `<button type="button" class="btn btn-outline-danger btn-sm del">X</button>`;
}
li += `</li>`;
ul.innerHTML += li;
}
// page 처리
let moreBtn= document.getElementById('moreBtn');
// 전체 페이지가 현재 페이지보다 크다면... (나와야 할 페이지가 존재한다면...)
if(result.pgvo.pageNo < result.realEndPage){
moreBtn.style.visibility = "visible"; // 표시
moreBtn.dataset.page = page + 1;
}else{
moreBtn.style.visibility = "hidden"; // 숨김
}
}else{
let li = `<li class="list-group-item">Comment List Empty</li>`;
ul.innerHTML = li;
}
});
};
document.addEventListener('click',(e)=> {
if(e.target.id == 'moreBtn'){
let page = parseInt(e.target.dataset.page);
spreadCommentList(bnoVal, page);
}
if(e.target.classList.contains('del')){
let cno = e.target.closest('li').dataset.cno;
removeCommentToServer(cno).then(result => {
if(result > 0){
alert("삭제 성공!!");
spreadCommentList(bnoVal);
}
})
}
if(e.target.classList.contains('mod')){
let li = e.target.closest('li');
let modWriter = li.querySelector(".fw-bold").innerText;
document.getElementById('cmtWriterMod').innerText = modWriter;
let cmtText =li.querySelector('.fw-bold').nextSibling; // 쿼리 값의 같은 부모의 다른 형제 값
document.getElementById('cmtTextMod').value = cmtText.nodeValue; // 그냥 넣으면 Object로 입력
// 수정 버튼 id = cmtModBtn dataset
document.getElementById('cmtModBtn').setAttribute("data-cno", li.dataset.cno);
}
if(e.target.id == 'cmtModBtn'){
let cmtModData={
cno : e.target.dataset.cno,
content : document.getElementById('cmtTextMod').value
}
console.log(cmtModData);
// 비동기 처리
updateCommentToServer(cmtModData).then(result =>{
if(result > 0){
alert("수정 성공!!");
}
// 모달창 닫기
document.querySelector('.btn-close').click();
spreadCommentList(bnoVal);
});
}
});
async function updateCommentToServer(cmtModData) {
try{
const url = "/comment/update"
const config ={
method : "put",
headers : {
'content-type':'application/json; charset=utf-8'
},
body : JSON.stringify(cmtModData)
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
async function removeCommentToServer(cno) {
try{
const url = "/comment/remove/"+cno;
const config={
method:'delete'
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch(error){
console.log(error);
}
}
async function postCommentToServer(cmtData) {
try {
const url = "/comment/post";
const config = {
method: "post",
headers:{
'Content-Type':'application/json; charset=utf-8'
},
body: JSON.stringify(cmtData)
};
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch (error) {
console.log(error);
}
}
async function getCommentListFromServer(bnoVal, page) {
try{
const resp = await fetch("/comment/list/"+bnoVal+"/"+page);
const result = await resp.json();
return result;
} catch(error){
console.log(error);
}
}
◈ UserController.java
package com.ezen.spring.controller;
import com.ezen.spring.domain.UserVO;
import com.ezen.spring.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.security.Principal;
@Slf4j
@RequestMapping("/user/*")
@RequiredArgsConstructor
@Controller
public class UserController {
private final UserService usv;
private final PasswordEncoder passwordEncoder;
@GetMapping("/signup")
public String signup(){
return "/user/register";
}
@PostMapping("/signup")
public String singup(UserVO userVO){
log.info(">>>> userVO {}",userVO);
userVO.setPwd(passwordEncoder.encode(userVO.getPwd()));
int isOk = usv.insert(userVO);
return "/index";
}
@GetMapping("/login")
public String login(){
return "/user/login";
}
@GetMapping("/list")
public String list(Model m){
m.addAttribute("userList",usv.getList());
return "/user/list";
}
@GetMapping("/modify")
public void modify(){}
@PostMapping("/modify")
public String modify(UserVO userVO){
log.info(">>> userVO >>> {}", userVO);
int isOk = 0;
if(userVO.getPwd() == null || userVO.getPwd().isEmpty()){
isOk = usv.modifyNoPwd(userVO);
} else{
userVO.setPwd(passwordEncoder.encode(userVO.getPwd()));
isOk = usv.modify(userVO);
}
return "redirect:/user/logout";
}
@GetMapping("/remove")
public String remove(Principal principal){
log.info(">>> principal >> {}", principal);
int isOk = usv.remove(principal.getName());
return "redirect:/user/logout";
}
@GetMapping("/last")
public String lastUpdate(Principal principal){
int isOk = usv.lastUpdate(principal.getName());
return "redirect:/user/logout";
}
}
◈ UserService.java
package com.ezen.spring.service;
import com.ezen.spring.domain.UserVO;
import java.util.List;
public interface UserService {
int insert(UserVO userVO);
List<UserVO> getList();
int modifyNoPwd(UserVO userVO);
int modify(UserVO userVO);
int remove(String name);
int lastUpdate(String name);
}
◈ UserServiceImpl.java
package com.ezen.spring.service;
import com.ezen.spring.domain.UserVO;
import com.ezen.spring.repository.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService{
private final UserMapper userMapper;
@Transactional
@Override
public int insert(UserVO userVO) {
int isOk = userMapper.insert(userVO);
if(isOk > 0){
userMapper.authInsert(userVO.getEmail());
}
return isOk;
}
@Transactional
@Override
public List<UserVO> getList() {
List<UserVO> userList = userMapper.getList();
for(UserVO userVO : userList){
userVO.setAuthList(userMapper.selectAuths(userVO.getEmail()));
}
log.info(">>> userList >>> {}", userList);
return userList;
}
@Override
public int modifyNoPwd(UserVO userVO) {
return userMapper.updateNoPwd(userVO);
}
@Override
public int modify(UserVO userVO) {
return userMapper.update(userVO);
}
@Override
public int remove(String name) {
int isOk = userMapper.removeAuth(name);
if(isOk > 0){
userMapper.remove(name);
}
return isOk;
}
@Override
public int lastUpdate(String name) {
return userMapper.lastUpdate(name);
}
}
◈ UserMapper.java
package com.ezen.spring.repository;
import com.ezen.spring.domain.AuthVO;
import com.ezen.spring.domain.UserVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
int insert(UserVO userVO);
void authInsert(String email);
UserVO selectEmail(String username);
List<AuthVO> selectAuths(String username);
List<UserVO> getList();
int updateNoPwd(UserVO userVO);
int update(UserVO userVO);
int remove(String name);
int removeAuth(String name);
int lastUpdate(String name);
}
◈ userMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ezen.spring.repository.UserMapper">
<insert id="insert">
insert into user(email, pwd, nick_name)
value(#{email}, #{pwd}, #{nickName})
</insert>
<insert id="authInsert">
insert into auth(email, auth)
values(#{email}, 'ROLE_USER')
</insert>
<select id="selectEmail" resultType="com.ezen.spring.domain.UserVO">
select * from user where email = #{email}
</select>
<select id="selectAuths" resultType="com.ezen.spring.domain.AuthVO">
select * from auth where email = #{email}
</select>
<select id="getList" resultType="com.ezen.spring.domain.UserVO">
select * from user
</select>
<update id="updateNoPwd">
update user set nick_name = #{nickName} where email = #{email}
</update>
<update id="update">
update user set pwd=#{pwd}, nick_name = #{nickName} where email= #{email}
</update>
<delete id="remove">
delete from user where email = #{email}
</delete>
<delete id="removeAuth">
delete from auth where email = #{email}
</delete>
<update id="lastUpdate">
update user set last_login = now() where email = #{email}
</update>
</mapper>
▷ 출력
'Java > Spring Boot' 카테고리의 다른 글
Spring Boot 기초(comment) - AWS 풀스택 과정 80일차 (0) | 2024.11.19 |
---|---|
Spring Boot 기초(file) - AWS 풀스택 과정 79일차 (0) | 2024.11.18 |
Spring Boot 기초(paging) - AWS 풀스택 과정 78일차 (0) | 2024.11.15 |
Spring Boot 기초(DB) - AWS 풀스택 과정 77일차 (0) | 2024.11.14 |
Spring Boot 설정 및 기초 - AWS 풀스택 과정 76일차 (0) | 2024.11.13 |