SpringBoot에서 Interceptor 활용 세션 로그인 구현 방법
Spring 세션 로그인 개발
LoginInterceptor.java 생성
import com.chunjae.archive_cms.common.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (SessionUtil.isLogin()) {
return true;
} else {
// 로그인이 되어있지 않은 경우 보여줄 페이지
response.sendRedirect("/user/login");
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 로그아웃 후 뒤로가기 시 로그인했던 상태가 남아있지 않게 no-cache 설정
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1
response.setHeader("Pragma", "no-cache"); // HTTP 1.0
response.setDateHeader("Expires", 0); // Proxy
}
}
HandlerInterceptor를 구현하고, preHandle 함수를 오버라이딩 해서 로그인 여부 체크를 체크합니다.
preHandle
: 모든 컨트롤러 호출 전 실행되는 함수입니다.
postHandle
: 컨트롤러 실행 후 호출되는 함수입니다.
WebConfig.java 생성
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 인터셉트를 적용할 페이지
.excludePathPatterns("/", "/user/login", "/user/loginProc", "/user/logout") // 인터셉트를 제외할 페이지들
.excludePathPatterns("/images/**", "/inc/**", "/html/**"); // 인터셉트를 제외할 webapp 이하 폴더들
}
}
WebMvcConfigurer를 구현하여 인터셉터를 등록하면서, 인터셉터 적용/미적용 페이지를 설정합니다.
SessionUtil.java 생성 (필수X)
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpSession;
import java.util.Map;
@Slf4j
@Component
public class SessionUtil {
@Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
public static HttpSession getSession() {
ServletRequestAttributes servletRequestAttribute = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return servletRequestAttribute.getRequest().getSession();
}
public static void setAttribute(String key, Object attribute) {
SessionUtil.getSession().setAttribute(key, attribute);
}
public static Map<String,Object> getLoginedUser() {
HttpSession session = SessionUtil.getSession();
return (Map<String, Object>) session.getAttribute("loginUser");
}
public static boolean isLogin () {
return SessionUtil.getLoginedUser() != null;
}
}
로그인 세션을 쉽게 관리하기 위한 유틸입니다.
Controller 예시
import com.chunjae.archive_cms.common.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private SessionUtil sessionUtil;
@GetMapping("/login")
public String getLoginPage() throws Exception {
return "user/login";
}
@ResponseBody
@PostMapping("/loginProc")
public Map<String, Object> login(@RequestParam("id") String id, @RequestParam("pw") String pw) throws Exception {
Map<String, Object> resultMap = new HashMap<>();
Boolean loginSuccess = false;
try {
// 유저 정보 조회
Map<String, Object> loginUser = userService.login(id, pw);
if(loginUser != null) {
sessionUtil.setAttribute("loginUser", loginUser);
loginSuccess = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
resultMap.put("success", loginSuccess);
}
return resultMap;
}
@ResponseBody
@PostMapping("/logout")
public Map<String, Object> logout() {
Map<String, Object> resultMap = new HashMap<>();
try {
// 세션 무효화, 세션의 모든 속성 삭제
SessionUtil.getSession().invalidate();
resultMap.put("success", true);
} catch (Exception e){
resultMap.put("success", false);
}
return resultMap;
}
@GetMapping("/pwChange")
public String getPwChangePage() throws Exception {
return "user/pwChange";
}
@ResponseBody
@PostMapping("/pwCheck")
public Map<String, Object> pwCheck(@RequestParam("id") String id, @RequestParam("pw") String pw) {
Map<String, Object> resultMap = new HashMap<>();
Boolean success = false;
try {
// 기존 비밀번호 체크
Map<String, Object> userName = userService.pwCheck(id, pw);
if(userName != null) {
success = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
resultMap.put("success", success);
}
return resultMap;
}
@ResponseBody
@PostMapping("/pwChangeProc")
public Map<String, Object> pwChangeProc(@RequestParam("id") String id, @RequestParam("new_pw") String new_pw) {
Map<String, Object> resultMap = new HashMap<>();
Boolean success = false;
try {
// 비밀번호 변경
Integer updateResult = userService.pwChange(id, new_pw);
if(updateResult > 0) {
success = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
resultMap.put("success", success);
}
return resultMap;
}
}
세션 로그인, 로그아웃, 비밀번호 변경 등의 기능을 구현하는 컨트롤러 예시입니다.
SessionUtil 없이 세션에 값 저장하는 방법
@ResponseBody
@PostMapping("/saveSessionData")
public Map<String, Object> saveSessionData(HttpSession httpSession, @RequestParam HashMap<String, String> param) {
Map<String, Object> resultMap = new HashMap<>();
try {
// 세션에 파라미터 값 저장
httpSession.setAttribute("키", param.get("값"));
resultMap.put("success", true);
} catch (Exception e){
resultMap.put("success", false);
}
return resultMap;
}
HttpSession 객체를 사용하여 직접 세션에 값을 저장해도 됩니다.
Service 예시
import com.chunjae.archive_cms.common.util.EncryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Slf4j
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private EncryptUtil encryptUtil;
public Map<String, Object> login(String id, String pw) throws Exception {
pw = encryptUtil.getSHA512(pw);
return userMapper.login(id, pw);
}
public Map<String, Object> pwCheck(String id, String pw) throws Exception {
pw = encryptUtil.getSHA512(pw);
return userMapper.pwCheck(id, pw);
}
public Integer pwChange(String id, String new_pw) throws Exception {
new_pw = encryptUtil.getSHA512(new_pw);
return userMapper.pwChange(id, new_pw);
}
}
Mapper 예시
import org.apache.ibatis.annotations.Mapper;
import java.util.Map;
@Mapper
public interface UserMapper {
Map<String, Object> login(String id, String pw) throws Exception;
Map<String, Object> pwCheck(String id, String pw) throws Exception;
Integer pwChange(String id, String new_pw) throws Exception;
}
Mybatis 쿼리 예시
<mapper namespace="com.chunjae.archive_cms.user.UserMapper">
<select id="login" parameterType="java.util.HashMap" resultType="java.util.HashMap">
SELECT
id
,name
,role_cd
,dept_name
FROM
유저테이블명
WHERE
id = #{id}
AND
pw = #{pw}
AND
delete_yn = 'N'
</select>
<select id="pwCheck" parameterType="java.util.HashMap" resultType="java.util.HashMap">
SELECT
name
FROM
유저테이블명
WHERE
id = #{id}
AND
pw = #{pw}
</select>
<update id="pwChange" parameterType="java.util.HashMap">
UPDATE
유저테이블명
SET
pw = #{new_pw}
WHERE
id = #{id}
</update>
</mapper>
로그인 화면 예시
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<body>
<div class="wrap bg-gray h100">
<jsp:include page="../common/layout/loginHeader.jsp"/>
<div class="container">
<div class="inner">
<div class="login">
<div class="box-login">
<span class="title">로그인</span>
<div class="form-input">
<form id="loginForm" name="loginForm">
<input id="id" name="id" type="text" placeholder="아이디" value="${cookie.userId.value}">
<input id="pw" name="pw" type="password" placeholder="비밀번호">
<input id="idSaveYn" name="idSaveYn" type="checkbox" name="" <c:if test="${cookie.idSaveYn.value eq 'Y'}">checked="true"</c:if>>
<label for="idSaveYn">아이디 저장</label>
</form>
</div>
<button class="btn size01" onclick="login();">로그인</button>
</div>
<em>※ 아이디/비밀번호가 기억나지 않을 경우, 담당자에게 문의하시길 바랍니다.<br>담당자: 고길동 (02-0000-0000)</em>
</div>
</div>
</div>
</div>
</body>
<script>
// 엔터 키 이벤트 발생 시 로그인
document.addEventListener("keyup", function(event) {
if (event.key === 'Enter') {
login();
}
});
function login() {
const formData = new FormData(document.getElementById("loginForm"));
if(!document.getElementById('id').value) {
alert("아이디를 입력해주세요.");
return;
}
if(!document.getElementById('pw').value) {
alert("비밀번호를 입력해주세요.");
return;
}
// 로그인 처리
fetch('/user/loginProc', {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((data) => {
if(data.success == true) {
// CookieUtil.js 사용하여 365일간 쿠키 저장
if ($("#idSaveYn").prop("checked") === true) {
setCookie("userId", $("#id").val().trim(), 365);
setCookie("idSaveYn", "Y", 365);
} else {
deleteCookie("userId");
deleteCookie("idSaveYn");
}
// 메인으로 이동
location.href = "/";
}else {
alert("아이디/비밀번호를 다시 한번 입력해주세요.");
}
})
.catch((error) => {
alert("로그인 할 수 없습니다. 관리자에게 문의해주세요.");
})
}
</script>
</html>
로그인 화면 JSP 예시입니다.
CookieUtil.js 예시
function setCookie(name, value, exp) {
const date = new Date();
date.setTime(date.getTime() + exp * 24 * 60 * 60 * 1000);
document.cookie = name + '=' + value + ';expires=' + date.toUTCString() + ';path=/';
}
function getCookie(name) {
var value = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return value? value[2] : null;
}
function deleteCookie(name) {
// 만료일을 과거로 변경하여 쿠키 삭제
document.cookie = name + '=; expires=Thu, 01 Jan 1999 00:00:10 GMT;path=/;';
}
아이디 저장 기능 구현 시 사용하는 Javascript 쿠키 저장 유틸 예시입니다.
비밀번호 변경 화면 예시
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<body>
<div class="wrap bg-gray h100">
<jsp:include page="../common/layout/simpleHeader.jsp"/>
<div class="container">
<div class="inner">
<div class="inquiry">
<div class="title-wrap">
<span class="title">비밀번호 수정</span>
<span class="txt">새로운 비밀번호를 입력해주세요.</span>
</div>
<div class="box-pwinquiry">
<div class="form-input">
<form id="pwForm" name="pwForm">
<div>
<span class="sub-tit">기존 비밀번호</span>
<input id="pw" name="pw" type="password">
</div>
<div>
<span class="sub-tit">새 비밀번호</span>
<input id="new_pw" name="new_pw" type="password" onchange="pwSameCheck();">
</div>
<div>
<span class="sub-tit">비밀번호 확인</span>
<input id="new_pw_check" name="new_pw_check" type="password" onchange="pwSameCheck();">
<em class="error" style="display:none;">* 비밀번호를 다시 한번 입력해주세요.</em>
</div>
</form>
</div>
</div>
<button class="btn size01" onclick="pwChange();">확인</button>
</div>
</div>
</div>
</div>
<script>
function pwSameCheck() {
const pw = document.getElementById('new_pw').value;
const new_pw = document.getElementById('new_pw_check').value;
if(pw === new_pw) {
$('.error').attr("style", "display:none;");
}else {
$('.error').attr("style", "display:inline-block;");
}
}
function pwChange() {
const formData = new FormData(document.getElementById("pwForm"));
if(!document.getElementById('pw').value) {
alert("기존 비밀번호를 입력해주세요.");
return;
}
const new_pw = document.getElementById('new_pw').value;
const new_pw_check = document.getElementById('new_pw_check').value;
if(!new_pw) {
alert("새 비밀번호를 입력해주세요.");
return;
}
if(!new_pw_check) {
alert("비밀번호 확인을 입력해주세요.");
return;
}
formData.append("id", "${sessionScope.loginUser.id}");
let errMsg = "비밀번호를 수정할 수 없습니다. 관리자에게 문의해주세요.";
// 기존 비밀번호 확인
fetch('/user/pwCheck', {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((data) => {
// 기존 비밀번호로 유저 조회되면
if(data.success == true) {
if(new_pw !== new_pw_check) {
alert("새 비밀번호, 비밀번호 확인이 다릅니다.");
return;
}
// 비밀번호 변경 처리
fetch('/user/pwChangeProc', {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((data) => {
// 비밀번호 변경 완료
if(data.success == true) {
alert("비밀번호가 변경되었습니다.");
// 메인으로 이동
location.href = "/";
}else {
alert(errMsg);
}
})
.catch((error) => {
alert(errMsg);
})
}else {
alert("기존 비밀번호를 다시 확인해주세요.");
}
})
.catch((error) => {
alert(errMsg);
})
}
</script>
</body>
</html>
비밀번호 변경 화면 JSP 예시입니다.
메뉴 헤더 예시
<c:if test="${sessionScope.loginUser.role_cd eq 0}">
<li>
<a href="/user">사용자 관리</a>
</li>
</c:if>
로그인 유저 권한에 따라 헤더 메뉴를 비활성화하는 예시입니다.
세션 만료 시간 설정
server.servlet.session.timeout=10800 #3시간 (초단위)
SpringBoot는 application.properties 파일에 세션 만료 시간을 설정할 수 있습니다.
Leave a comment