클라이언트의 로그인 여부와 로그인 했을경우 정보를 조회하는 방법
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository repository;
private final PasswordEncoder passwordEncoder;
....
//-- username 으로 user 조회 --//
public SiteUser getUser(String username) {
Optional<SiteUser> siteUser = repository.findByUsername(username);
if (siteUser.isPresent())
return siteUser.get();
else
throw new DataNotFoundException("SiteUser not found");
}
}
@Service
@RequiredArgsConstructor
public class AnswerService {
private final AnswerRepository answerRepository;
//-- answer 생성 --//
public void create(Question question, String content, SiteUser author) {
Answer answer = new Answer();
answer.setContent(content);
answer.setCreateDate(LocalDateTime.now());
answer.setQuestion(question);
answer.setAuthor(author);
this.answerRepository.save(answer);
}
}
@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
public class AnswerController {
//-- DI --//
private final QuestionService questionService;
private final AnswerService answerService;
private final UserService userService;
//-- Anser 생성 로직 --//
@PostMapping("/create/{id}")
public String createAnswer(
@PathVariable Integer id,
@Valid AnswerForm answerForm,
BindingResult bindingResult,
Principal principal, // Spring Security 가 제공하는 로그인한 사용자의 정보를 확인해주는 객체
Model model
) {
// answer 를 등록할 Question 을 조회하는 로직
Question question = this.questionService.getQuestion(id);
// 현재 로그인한 사용자의 username 을 조회해 SiteUser 를 찾는 로직
SiteUser siteUser = this.userService.getUser(principal.getName());
// 클라이언트가 입력 양식에 맞지 않게 작성할 경우 에러 반환
if (bindingResult.hasErrors()) {
model.addAttribute("question", question);
return "question_detail";
}
// answer 생성
this.answerService.create(question, answerForm.getContent(), siteUser);
return String.format("redirect:/question/detail/%s" , id);
}
}
<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container my-3">
<table class="table">
<thead class="table-dark">
<tr>
<th>번호</th>
<th style="width:50%">제목</th>
<th>글쓴이</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<!-- question list 시작 -->
<tr class="text-center" th:each="question, loop : ${paging}">
<!-- question 고유 번호 시작-->
<td th:text="${paging.getTotalElements - (paging.number * paging.size) - loop.index}"></td>
<!-- question 고유 번호 종료-->
<!-- question 제목 시작-->
<td class="text-start">
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
<!-- 게시물의 댓글 개수 표시 시작-->
<span class="danger small ms-2"
th:if="${#lists.size(question.answerList)} > 0"
th:text="${#lists.size(question.answerList)}">
</span>
<!-- 게시물의 댓글 개수 표시 종료-->
</td>
<!-- question 제목 종료-->
<!-- 글쓴이 표시 시작-->
<td>
<span th:if="${question.author != null}"
th:text="${question.author.username}"></span>
</td>
<!-- 글쓴이 표시 종료-->
<td th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
<!-- question list 종료 -->
</tbody>
</table>
<!-- 페이징 처리 시작 -->
<div th:if="${!paging.isEmpty()}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${!paging.hasPrevious()} ? 'disabled'">
<a class="page-link"
th:href="@{|?page=${paging.number-1}|}">
<span>이전</span>
</a>
</li>
<li th:each="page: ${#numbers.sequence(0, paging.totalPages-1)}"
th:if="${page >= paging.number-5 and page <= paging.number+5}"
th:classappend="${page == paging.number} ? 'active'"
class="page-item">
<a th:text="${page}" class="page-link" th:href="@{|?page=${page}|}"></a>
</li>
<li class="page-item" th:classappend="${!paging.hasNext} ? 'disabled'">
<a class="page-link"
th:href="@{|?page=${paging.number+1}|}">
<span>다음</span>
</a>
</li>
</ul>
</div>
<!-- 페이징 처리 종료-->
<a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a>
</div>
</html>
<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container my-3">
<!-- 질문 -->
<h2 class="border-bottom py-2" th:text="${question.subject}"></h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${question.content}"></div>
<div class="d-flex justify-content-end">
<!-- 날자, 글슨이 정보 시작-->
<div class="badge bg-light text-dark p-2 text-start">
<!-- 글쓴이 정보 추가 시작-->
<div class="mb-2">
<span th:if="${question.author != null}"
th:text="${question.author.username}"></span>
</div>
<!-- 글쓴이 정보 추가 종료-->
<div th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
<!-- 날자, 글슨이 정보 종료-->
</div>
</div>
</div>
<!-- 답변의 갯수 표시 -->
<h5 class="border-bottom my-3 py-2"
th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<!-- 답변 반복 시작 -->
<div class="card my-3" th:each="answer : ${question.answerList}">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${answer.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<!-- 답변자 정보 추가 시작-->
<div class="mb-2">
<span th:if="${answer.author != null}"
th:text="${answer.author.username}"></span>
</div>
<!-- 답변자 정보 추가 종료-->
<div th:text="${#temporals.format(answer.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
<!-- 답변 작성 -->
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post" class="my-3">
<!-- 오류 처리 탬플릿 사용 시작-->
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
<!-- 오류 처리 탬플릿 사용 종료-->
<textarea th:field="*{content}"
class="form-control"
rows="10"></textarea>
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>
</div>
</html>
princial.getName
으로 username 을 조회하는데 princial
읙 객체가 null (로그아웃 상태) 일경우 발생하는 오류이다.@PreAuthorize("isAuthenticated()")
@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
public class AnswerController {
private final QuestionService questionService;
private final AnswerService answerService;
private final UserService userService;
@PostMapping("/create/{id}")
@PreAuthorize("isAuthenticated()") // 접근 권한 설정
public String createAnswer(
@PathVariable Integer id,
@Valid AnswerForm answerForm,
BindingResult bindingResult,
Principal principal,
Model model
) {
Question question = this.questionService.getQuestion(id);
SiteUser siteUser = this.userService.getUser(principal.getName());
if (bindingResult.hasErrors()) {
model.addAttribute("question", question);
return "question_detail";
}
this.answerService.create(question, answerForm.getContent(), siteUser);
return String.format("redirect:/question/detail/%s" , id);
}
}
@EnableMethodSecurity(prePostEnabled = true)
@PreAuthorize
의 기능을 사용하기위해 반드시 필요하다.@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // 접근 권한 설정
public class SecurityConfig {
disabled
sec:authorize="isAnonymous()”
sec:authorize="isAuthenticated()"
<html layout:decorate="~{layout}" xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.w3.org/1999/xhtml">
<div layout:fragment="content" class="container my-3">
<!-- 질문 -->
<h2 class="border-bottom py-2" th:text="${question.subject}"></h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${question.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#temporals.format(question.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
</div>
</div>
<!-- 답변의 갯수 표시 -->
<h5 class="border-bottom my-3 py-2"
th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<!-- 답변 반복 시작 -->
<div class="card my-3" th:each="answer : ${question.answerList}">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${answer.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#temporals.format(answer.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
<!-- 답변 작성 -->
<form th:action="@{|/answer/create/${question.id}|}" th:object="${answerForm}" method="post" class="my-3">
<!-- 오류 처리 탬플릿 사용 시작-->
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
<!-- 오류 처리 탬플릿 사용 종료-->
<!-- log-out 클라이언트에게 답변 공간 비공개 시작-->
<textarea sec:authorize="isAnonymous()" disabled
th:field="*{content}"
class="form-control"
rows="10"></textarea>
<textarea sec:authorize="isAuthenticated()"
th:field="*{content}"
rows="10"
class="form-control" ></textarea>
<!-- log-out 클라이언트에게 답변 공간 비공개 시작-->
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>
</div>
</html>