스프링으로 쇼핑몰을 만들어보자17 - 인터셉터 적용
기능구현
- 회원가입(주소API연동, 이메일 인증, ajax를 사용하여 아이디 중복검사)
- 관리자 페이지(인터셉터 적용, 상품관리, 회원관리)
- 업로드(이미지 등록, 수정, 삭제)
- 검색
- 페이징
- 구매(장바구니, 포인트사용)
- 댓글(등록,수정,삭제)
- 중간에 막혔거나 에러 해결 못하겠으면 yoonbitnara@gmail.com 으로 문의
지난 포스팅에서 관리자 페이지를 만들었다. 메인 페이지에서 관리자 계정으로만 로그인하였을때만 관리자 페이지로 이동할 수 있는 링크가 보이도록 하였다.
하지만 일반 계정 혹은 로그인하지 않더라도 관리자 페이지 url만 알고 있다면 접속을 할 수 있게 된다.
따라서 AdminController.java
에 있는 관리자 페이지 접속 메서드에 세션 체크를 통해 권한이 없는 관리자가 접근 시 메서드 실행이 되지 않도록 로직을 작성해야 한다.
문제는 해당 몇몇의 메서드만 적용해야 한다면 문제가 없다.하지만 AdminController.java
에서 작성된 메서드들은 관리자가 사용할 메서드들이기 때문에 작성될 메서드 전체에 사용자 권한을 확인하는 코드를 중복하여 작성해야 한다.
이러한 세션 체크 로직처럼 웹을 실행하기 위한 핵심 로직은 아니지만 반드시 필요한 로직들을 한번의 작성으로 일괄적으로 관리해 줄 수 있도록 하는 수단으로써 인터셉터가 있다. 인터셉터는 Controller를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 일종의 필터이다.
- 인터셉터 적용 대상은 2개이다.
AdminController.java
전체MemberController.java
의 로그인 메서드
순서
- 변경사항
- 인터셉터 기본 설정
LoginInterceptor.java
구현AdminController.java
구현- 테스트
관리자 페이지
- 다음 포스팅부터 관리자 페이지를 진행하기 위해 관리자 페이지의 기본적인 틀을 만들었다. 지난 포스팅에서 만든 admin폴더 경로에 있는 main.jsp를 아래의 코드로 변경하였다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>관리자페이지</title>
<link rel="stylesheet" href="../resources/css/admin/main.css">
<script
src="https://code.jquery.com/jquery-3.4.1.js"
integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
crossorigin="anonymous"></script>
</head>
<body>
<div class="wrapper">
<div class="wrap">
<!-- gnv_area -->
<div class="top_gnb_area">
<ul class="list">
<li><a href="/main">메인 페이지</a></li>
<li><a href="/member/logout.do">로그아웃</a></li>
<li>고객센터</li>
</ul>
</div>
<!-- top_subject_area -->
<div class="admin_top_wrap">
<span>관리자 페이지</span>
</div>
<!-- contents-area -->
<div class="admin_wrap">
<!-- 네비영역 -->
<div class="admin_navi_wrap">
<ul>
<li >
<a class="admin_list_01">상품 등록</a>
</li>
<li>
<a class="admin_list_02">상품 목록</a>
</li>
<lI>
<a class="admin_list_03">작가 등록</a>
</lI>
<lI>
<a class="admin_list_04">작가 관리</a>
</lI>
<lI>
<a class="admin_list_05">회원 관리</a>
</lI>
</ul>
<!--
<div class="admin_list_01">
<a>상품 관리</a>
</div>
-->
</div>
<div class="admin_content_wrap">
<div>관리자 페이지 입니다.</div>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</body>
</html>
css는 아래와 같이 작성하였다.
@charset "UTF-8";
*{
margin: 0;
padding:0;
}
a{
text-decoration: none;
}
ul{
list-style: none;
}
/* 화면 전체 렙 */
.wrapper{
width: 100%;
}
/* content 랩 */
.wrap{
width : 1080px;
margin: auto;
}
/* 홈페이지 기능 네비 */
.top_gnb_area{
width: 100%;
height: 50px;
background-color: #f0f0f1;
position:relative;
}
.top_gnb_area .list{
position: absolute;
top: 0px;
right: 0;
}
.top_gnb_area .list li{
list-style: none;
float : left;
padding: 13px 15px 0 10px;
font-weight: 900;
cursor: pointer;
}
/* 관리제 페이지 상단 현페이지 정보 */
.admin_top_wrap{
height:110px;
line-height: 110px;
background-color: #5080bd;
}
.admin_top_wrap>span{
margin-left: 30px;
display:inline-block;
color: white;
font-size: 50px;
font-weight: bolder;
}
/* 관리자 wrap(네비+컨텐츠) */
.admin_wrap{
}
/* 관리자페이지 네비 영역 */
.admin_navi_wrap{
width: 20%;
height: 300px;
float:left;
height: 100%;
}
.admin_navi_wrap li{
display: block;
height: 80px;
line-height: 80px;
text-align: center;
}
.admin_navi_wrap li a{
display: block;
height: 100%;
width: 95%;
margin: 0 auto;
cursor: pointer;
font-size: 30px;
font-weight: bolder;
}
/*
.admin_list_01{
background-color: #c8c8c8;
} */
/* 관리자페이지 컨텐츠 영역 */
.admin_content_wrap{
width: 80%;
float:left;
height: 100%;
height: 700px;
}
.admin_content_wrap div{
margin-top: 280px;
text-align: center;
font-size: 50px;
font-weight: bolder;
}
/* float 속성 해제 */
.clearfix{
clear: both;
}
/admin/main.jsp
의 gnb영역에 있는 로그아웃은 스크립트 태그를 통해 post방식의/member/logout
을 요청하는 것이 아니라 GET 방식의/member/logout
으로 변경하였다.
관리자 페이지에서 화면 이동 없이 로그아웃을 해도 크게 문제는 생기지 않는다. 왜냐하면 로그아웃 후/admin
경로의 url을 요청하더라도 이번 포스팅에서 적용할 인터셉터에 의해 main페이지로 강제 이동될 것이기 때문이다.
그렇지만 관리자 페이지에 있는 상황에서 로그아웃이 되었음을 좀 더 명확히 하기 위해 로그아웃 후 메인 페이지로 이동시키는 GET방식의/member/logout.do
url을 사용하였다.
<div class="top_gnb_area">
<ul class="list">
<li><a href="/main">메인 페이지</a></li>
<li><a href="/member/logout.do">로그아웃</a></li>
<li>고객센터</li>
</ul>
</div>
로그인 url 변경
- 기존 로그인 url을 쓰는데 전혀 문제가 없지만 이번 포스팅에서 사용할 인터셉터의 적용 대상을 정확히 타겟팅 하기 위해서 로그인 url을 약간 수정하겠다.
POST방식의 로그인 url을 기존 login에서 login.do로 변경한다. 변경할 곳은 두 곳이다.MemberController.java
에@RequestMapping
어노테이션 부분,login.jsp
에 있는 스크립트 태그 내에 서버에 로그인 요청하는 부분을 변경한다.
- MemberController.java
- login.jsp
Interceptor 기본 설정
- Interceptor를 사용하기 위해 전체적으로 두 가지의 작업이 필요하다.
- spring-web 라이브러리를 pom.xml에 추가한다.
- 적용할 Interceptor 클래스를 작성하고 이를 적용하기 위해 servlet-context.xml에 적용시킬 경로와 적용할 클래스를 설정한다.
spring-webmvc 라이브러리 추가
- spring-web 라이브러리를 추가한다. 하지만 이미 spring-web라이브러리를 포함하고 있는 spring-webmvc를 추가했기 때문에 따로 추가할 필요는 없다.
Interceptor 클래스 작성
- Interceptor 클래스를 따로 관리하기 위해
com.store.interceptor
패키지를 새로 만들었다.
새로 만든 패키지에 LoginInterceptor.java
, AdminInterceptor.java
클래스를 생성한다.
servlet-context.xml 설정
- 우리가 적용할 대상은
/admin
경로가 붙는 모든 url, 로그인을 수행하는login.do
url이다. 따라서 servlet-context.xml에 해당 url에 적용되도록 코드를 추가한다. 추가적으로 우리가 생성한 Interceptor 클래스가 Spring에서 인식하여 적용이 될 수 있도록 bean 코드도 추가한다.
<interceptors>
<interceptor>
<mapping path="/member/login.do"/>
<beans:bean id="LoginInterceptor" class="com.store.interceptor.LoginInterceptor"></beans:bean>
</interceptor>
<interceptor>
<mapping path="/admin/**"/>
<beans:bean id="AdminInterceptor" class="com.store.interceptor.AdminInterceptor"></beans:bean>
</interceptor>
</interceptors>
LoginInterceptor.java
- 일어날 확률은 매우 낮지만 이전 작업 중 세션이 완전히 제거되지 않아 로그인을 위해 새로운 세션을 저장할 때 발생할 수 있는 에러를 방지하기 위해 로그인 메서드가 있는
MemberController.java
에 진입하기 전 세션을 제거하는 작업을 해주고자 한다. - 먼저 해당 클래스를 interceptor로 사용하기 위해 클래스 선언부에 HandlerInterceptor 상속을 선언한다.
package com.store.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor{
}
- 우리가 하고자 하는 작업은 Controller에 진입하기 전 작업을 원하기 때문에
preHandle()
메서드를 오버라이딩한다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
preHandle()
메서드 구현부에 세션을 제거해주는 코드를 추가한다.preHandle()
이 정상적으로 작동했는지 확인하기 위해서println()
메서드를 호출했다.println()
메서드는 정상적으로 실행이 되는지 확인 후 주석처리하거나 삭제한다.
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("LoginInterceptor preHandle start");
HttpSession session = request.getSession();
session.invalidate();
return true;
}
AdminInterceptor.java 구현
- 관리자 메서드
("/admin/**")
에 접근하는 사용자의 adminCk가 1인지 확인하는 작업이 핵심이다. 따라서 member session 정보를MemberVO
타입의 변수에 담은 후 해당 변수를 통해 adminCk의 값을 호출하여 비교하는 로직을 작성해주어야 한다. LoginInterceptorController.java
와 동일하게HandlerInterceptor
클래스를 상속시키고preHandle()
메서드를 오버라이딩한다.
package com.store.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
public class AdminInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
}
- 먼저 member session을 호출하여
MemberVO
타입의 lvo 변수에 저장한다. (MemberVO타입으로 형변환 해주어야 함)
HttpSession session = request.getSession();
MemberVO lvo = (MemberVO)session.getAttribute("member");
- if문을 통해 lvo가 null이거나 getAdminCk()메서드 반환 값이 0이면 main페이지로 리다이렉트 되도록 코드를 추가 후 false를 반환하도록 로직을 작성한다.
- 아닐 경우
AdminController.java
에 접근할 수 있도록 true를 반환하도록 작성한다.
package com.store.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import com.store.model.MemberVO;
public class AdminInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
MemberVO lvo = (MemberVO)session.getAttribute("member");
if (lvo == null || lvo.getAdminCk() == 0) { // 관리자 계정이 아닌 경우
response.sendRedirect("/main"); // 메인페이지로 리다이렉트
return false;
}
return true;
}
}
테스트
로그인 인터셉터 테스트
- 관리자 계정으로 로그인 하여 테스트를 한다.
- 로그인을 하지 않은 상태에서 /admin/main을 접속해본다.
- 바로 main으로 리다이렉트 되는 것을 볼 수 있다.
- 일반계정도 마찬가지 일거다.
Leave a comment