만약 로그인하지 않은 회원은 글 등록을 못하게 하고 싶다고 가정한다.
[header.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%-- jstl의 core 라이브러리를 사용하기 위해 taglib를 이용한다. --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%--절대경로를 쉽게 사용하기 위해 session에 root를 key로 컨텍스트 루트 경로를 저장해둔다. --%>
<c:set value="${pageContext.request.contextPath }" scope="session"
var="root"></c:set>
<!-- 로그인을 안했을 경우 -->
<c:if test="${empty loginUser}">
<form action="login" method="post">
<input type="text" name="id"> <input type="text" name="pass">
<input type="submit" value="로그인">
</form>
</c:if>
<!-- 로그인을 했을 경우 -->
<c:if test="${not empty loginUser}">
안녕하세요 ${loginUser.name}님 <a href="logout">로그아웃</a>
</c:if>
<hr>
<!-- 로그인했습니다/안했습니다 알림창 -->
<script>
let msg = "${msg}";
if (msg) {
alert(msg)
}
</script>
모든 페이지 상단은 동일하기 때문에 중복해서 코드 칠 필요없이 header.jsp에 하나로 적어 모든 페이지에 적용하는 방식으로 진행한다.
header.jsp를 메인 페이지에 적용한다.
[index.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>도서 관리</title>
<style>
</style>
</head>
<body>
<%-- header.jsp를 include해서 재사용하기 --%>
<%@ include file="/WEB-INF/views/include/header.jsp"%>
<div class="container">
<ul>
<li><a href="${root }/regist">도서 등록</a></li>
<li><a href="${root }/list">도서 목록</a></li>
</ul>
</div>
</body>
</html>
<%@ include file="파일 경로" %>를 이용해 메인페이지에 header.jsp를 적용한다.
로그인 안 한 회원의 경우 도서 등록을 못하게 막을 것이다.
=> 때문에 HandlerInterceptor 인터페이스를 구현할 SessionInterceptor 클래스를 만들어 줄 겁니다.
[SessionInterceptor.java]
@Component
public class SessionInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
HttpSession session = request.getSession();
if(session.getAttribute("loginUser")!=null) {
return true;
}else {
response.sendRedirect("index");
return false;
}
}
}
위 import는 모두 생략했다.
HandlerInterceptor는 유저 요청이 컨트롤러로 가기 이전에 인터셉터하여 true/false를 반환하는 preHandle과 컨트롤러 시행 중 인터셉터하여 결과를 처리하는 postHandle, 그리고 최종적으로 뷰 단에서 최종 결과를 컨트롤러로 보낼 때 적용하는 afterCompletion이 있다.
유저가 글등록 요청(regist) > 컨트롤러에서 그에 맞는 응답 forward를 해 글등록을 할 수 있는 페이지로 넘어감
이 과정을 거치는데, 글등록 페이지로 넘어가기 전에 회원 체크를 해 회원인 애만 넘어가게 하고 싶기 때문에 preHandle을 것이다.
[Controller.java]
@PostMapping("login")
public String doLogin(User user, Model model, HttpSession session) {
if(user.getId().equals("some_id") && user.getPass().equals("1234")) {
user.setName("shinsanha");
session.setAttribute("loginUser", user);
model.addAttribute("msg","로그인이 완료되었습니다.");
}
return "redirect:index";
}
컨트롤러에서는 유저가 로그인 시도를 해서 로그인 정보가 맞으면 세션에 loginUser로 유저 정보를 저장하게 했다. (현재는 유저 DB가 없다고 가정한다.)
때문에 SessionInterceptor.java에서 로그인 유저가 비었는지 비지 않았는지 체크해서 true or false를 넘겨 글 등록 페이지로 접근 가능하게 할지 말지를 결정하는 것이다.
여기까지가 로직의 끝이다.
이 로직을 작동하게 하려면 우리의 수문장 (dispature servlet)에게 이런 클래스를 사용할 것이다. 라고 bean과, interceptor 등록을 해줘야한다.
[servlet-context.xml]
<interceptors>
<interceptor>
<mapping path="/regist"></mapping>
<beans:bean class="com.ws.interceptor.SessionInterceptor"/>
</interceptor>
</interceptors>
수문장은 2가지 문을 관리한다. 1) servlet-context.xml (웹과 관련된 설정) 2) root-context.xml(그 외 설정)
로그인 체킹은 웹과 관련되어있으므로 servler-context.xml에 빈과 인터셉터를 등록한다.
class에는 src/main/java아래부터 풀 패키지명과 클래스명을 적어주면 된다.
mapping path는 이 인터셉터가 영향을 끼치는 경로를 적어준다. 글등록 페이지(regist)는 로그인 인증이 안되면 접근 못하게 할 것이니 regist를 등록한 것이다.
그럼 전체적으로 2가지 갈래길이 있는 것이다.
정상 경로 (로그인하고, 글등록 페이지 요청하는 경우)
유저 로그인 요청 > 디스패처 서블릿 중 servlet-context.xml 인터셉터 확인 > preHandle 작동 > mapping path에 등록된 regist 요청이 아니기 때문에 패스 > 컨트롤러 post / login 기능 수행 : 로그인 정보가 맞다면 로그인 완료 > 글등록 페이지 요청 (post / regist) > 디스패처 서블릿 중 servlet-context.xml 인터셉터 확인 > preHandle 작동 > mapping path에 등록된 regist 요청 맞는데, 세션에 등록된 loginUser가 있기 때문에 true반환 > 글등록 페이지 응답 완료
비정상 경로 (로그인 안하고, 글등록 페이지 요청하는 경우)
글등록 페이지 요청 (post / regist) > 디스패처 서블릿 중 servlet-context.xml 인터셉터 확인 > preHandle 작동 > mapping path에 등록된 regist 요청 맞는데, 세션에 등록된 loginUser가 없기 때문에 false반환 > 글등록 페이지 응답 거절