지난 포스팅에서 관리자 페이지를 만들었다. 메인 페이지에서 관리자 계정으로만 로그인하였을때만 관리자 페이지로 이동할 수 있는 링크가 보이도록 하였다.
하지만 일반 계정 혹은 로그인하지 않더라도 관리자 페이지 url만 알고 있다면 접속을 할 수 있게 된다.
따라서 AdminController.java
에 있는 관리자 페이지 접속 메서드에 세션 체크를 통해 권한이 없는 관리자가 접근 시 메서드 실행이 되지 않도록 로직을 작성해야 한다.
문제는 해당 몇몇의 메서드만 적용해야 한다면 문제가 없다.하지만 AdminController.java
에서 작성된 메서드들은 관리자가 사용할 메서드들이기 때문에 작성될 메서드 전체에 사용자 권한을 확인하는 코드를 중복하여 작성해야 한다.
이러한 세션 체크 로직처럼 웹을 실행하기 위한 핵심 로직은 아니지만 반드시 필요한 로직들을 한번의 작성으로 일괄적으로 관리해 줄 수 있도록 하는 수단으로써 인터셉터가 있다. 인터셉터는 Controller를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 일종의 필터이다.
AdminController.java
전체MemberController.java
의 로그인 메서드LoginInterceptor.java
구현AdminController.java
구현<%@ 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페이지로 강제 이동될 것이기 때문이다./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>
MemberController.java
에 @RequestMapping
어노테이션 부분, login.jsp
에 있는 스크립트 태그 내에 서버에 로그인 요청하는 부분을 변경한다.com.store.interceptor
패키지를 새로 만들었다.새로 만든 패키지에 LoginInterceptor.java
, AdminInterceptor.java
클래스를 생성한다.
/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>
MemberController.java
에 진입하기 전 세션을 제거하는 작업을 해주고자 한다.package com.store.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor{
}
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;
}
("/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;
}
}
MemberVO
타입의 lvo 변수에 저장한다. (MemberVO타입으로 형변환 해주어야 함) HttpSession session = request.getSession();
MemberVO lvo = (MemberVO)session.getAttribute("member");
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;
}
}