비회원이 작성한 비밀글에 대한 URL 직접 접근이 가능한 문제를 발견하였고, 이를 해결하기 위해 세션에서 검증이 완료되었는지 확인하는 단계를 추가하였다.
특히, 비밀번호 인증 성공 시 세션에 인증 정보를 저장, 이후 접근 시 인증 여부를 검증하는 로직을 구현하였다.
또한, 세션 유지 시간을 설정하는 방법에 대해서도 설명하였다.
✨시작하며
전자정부 프레임워크에서 프로젝트 수행 중 비회원이 작성한 비밀글에 대해 URL 직접접근이 가능한 것을 확인했다.
list로 불러올 경우 비밀글에 접근하려면 암호를 입력해야 하지만, view로 직접 URL을 입력해 접근하면 암호 입력 과정 없이 글에 접근할 수 있게 된다.
이렇게 되면 URL을 유추하거나 URL을 입력해 타인의 게시글에 접근이 가능해진다.
따라서 list에서 암호를 입력하여 session에서 검증이 완료되었는지 검증하는 단계를 추가하기로 했다.
코드 로직
대략적인 로직은 다음과 같다.
- list에서 비밀글을 클릭 시 나타나는 passwordCheckPop function에서 비밀번호 확인이 성공할 경우(비밀번호가 일치할 경우) Set<Integer> authorizedDocs를 생성한다.
- boardVO에서 DocSeq 값을 가져와 authorizedDocs에 추가한다.
- 이후 비회원 사용자가 게시글을 조회 시 view Controller에서 조건문을 통해 authorizedDocs에 docSeq가 포함되어 있지 않는다면 redirect로 사용자를 list 페이지로 돌려보낸다.
passwordCheckPop.do
list에서 팝업에 비밀번호를 입력하면 입력한 비밀번호가 게시글과 동일한지 검증한다.
동일하지 않다면 passwordCheck는 null이 된다.
String passwordCheck = boardService.passwordCheck(boardVO);
비밀번호가 동일했다면 passwordCheck는 null이 아닐 것이므로 조건문을 통해 passwordCheck의 value가 null이 아닌지 검증해 준다.
비밀번호가 동일하면 passwordCheck의 value는 docSeq일 것이며, 이후의 비밀글 인증을 위해 authorizedDocs에 session으로 authorizedDocs값을 가져와준다.
해당 과정은 이후 다시 설명하도록 하고 우선 다음 단계로 넘어가자.
authorizedDocs가 null이면 authorizedDocs를 새로운 HashSet 객체에 할당한다.
다음으로 session의 authorizedDocs attribute에 authorizedDocs(=docSeq) 값을 설정해 주고, authorizedDocs에 boardVO의 DocSeq를 설정해 준다.
이렇게 되면 authorizedDocs의 값은 docSeq의 값이 된다.
// BoardController.java > passwordCheckPop.do
// 비밀번호 확인이 성공한 경우
if(passwordCheck != null){
HttpSession session = request.getSession();
Set<Integer> authorizedDocs = (Set<Integer>) session.getAttribute("authorizedDocs");
if (authorizedDocs == null) {
authorizedDocs = new HashSet<>();
session.setAttribute("authorizedDocs", authorizedDocs);
}
authorizedDocs.add(boardVO.getDocSeq());
}
view.do
이후 @RequestMapping의 value가 view인 Controller에 parameter를 추가해야 한다.
해당 function에 @RequestParam 어노테이션으로 request param을 parameter에 바인딩을 해줘야 한다.
각 게시판에 존재하는 게시글은 docSeq에 게시글 번호로 저장되니 docSeq로 게시글을 구분하면 된다.
HttpServletRequest request, RedirectAttributes redirect, @ModelAttribute("boardVO") BoardVO boardVO, @RequestParam int docSeq) throws Exception {
이후 비회원을 검증하던 기존의 조건문 안에 내용을 조금 더 추가했다.
authorizedDocs를 session에서 가져온다.
위의 값을 예시로 authorizedDocs에 1567이 들어있다고 가정하자.
authorizedDocs는 docSeq인 1567 값과 동일하게 1567을 포함하고 있으므로 두 조건을 모두 일치한다.
따라서 조건문을 빠져나가 이후의 코드를 실행한다.
하지만, authorizedDocs의 값이 null이거나 해당 게시글의 docSeq값을 포함하고 있지 않을 경우 redirect를 통해 list로 돌아가게 된다.
참고로 아래 코드 중 비회원을 검증하는 코드와 return의 redirect는 일부분을 따로 제거했으므로 authorizedDocs를 검증하는 부분만 보면 된다.
Set<Integer> authorizedDocs = (Set<Integer>) session.getAttribute("authorizedDocs");
if (authorizedDocs == null || !authorizedDocs.contains(docSeq)) {
// 조회 권한이 없는 경우 에러 메시지를 표시하거나 리스트 페이지로 이동
redirect.addFlashAttribute("msg","올바른 경로가 아닙니다.");
return "redirect:/board/list.do";
}
여기서 docSeq값을 포함하고 있지 않다는 것은 비밀번호 인증을 성공한 적이 없는 것을 말한다.
앞서 위의 passwordCheckPop 로직에서 보았듯이 중간에 authorizedDocs의 session값을 가져오는 코드가 있다.
Set<Integer> authorizedDocs = (Set<Integer>) session.getAttribute("authorizedDocs");
if (authorizedDocs == null) {
authorizedDocs = new HashSet<>();
session.setAttribute("authorizedDocs", authorizedDocs);
}
authorizedDocs.add(boardVO.getDocSeq());
docSeq가 1567인 게시글 외에 다른 게시글에서 비밀번호 인증을 성공할 경우, authorizedDocs의 값은 1567일 것이므로 중간의 조건문에 부합하지 않으니 docSeq를 추가만 한다.
이후 view 코드가 실행될 때 다시 조건문에서 검증을 하는데, 현재 1569에 대한 게시글 view가 진행 중이라면 contains 조건에 부합하므로(authorizedDocs에 1569가 포함) 코드가 정상적으로 실행될 것이다.
문제는 authorizedDocs가 session에 저장되어 기존에 게시글에 대한 비밀번호 인증을 성공적으로 완료했다면 session에 정보가 담겨있다는 것이다.
게시글 조회 시 계속해서 인증을 요구할까 싶었는데, 사용자의 입장을 고려해 session에 담는 것이 더 올바르다고 판단하여 코드는 이후 별도로 수정하지 않았다!
session에 정보가 담겨있다면 session 유지 시간 동안은 비밀번호를 더는 입력하지 않아도 될 것이므로 비회원이 홈페이지를 이용 시 번거로운 절차를 거치지 않아도 될 것이다.
문제가 된다면 session 유지 시간을 변경하는 것도 나쁘지 않다는 생각이 들어 추가적으로 session 유지 시간을 설정하는 방법을 적어볼까 한다.
web.xml에서 session 유지 시간 설정하기
기본적으로 session의 유지 시간은 30분으로 되어있다.
분 단위로 원하는 시간을 지정하고, tomcat을 재시작하면 된다.
<!-- 세션 유지 시간(분 단위) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- 세션 유지 시간(분 단위) -->