웹 기능에 로그인은 매우 자주 사용되는 기능이다. spring에서는 이 로그인 기능을 세션을 사용해 어떤 멤버가 로그인하였는지, 인터셉터를 사용하여 로그인 안된(세션이 없는) 사용자는 서비스를 거부할 수 있다.
1. 세션
세션 생성은 HttpSession session을 사용하면 되고, 세션 조회는 @SessionAttribute(name="attributeName", required = false)을 사용하면 된다.
application.properties에 다음 내용을 추가해 세션을 조정할 수 있다.
# 세션 움직임 없을때 타임아웃 시간. 기본 30분이다.
server.servlet.session.timeout=60m
# 세션을 쿠키로만 받는다. 아니면 url로 전송될 때도 있다.
server.servlet.session.tracking-modes=cookie
다음과 같은 컨트롤러로 구성되어있다고 해보자.
@Controller
public class HomeController {
@GetMapping("/")
public String index(@SessionAttribute(name="id", required = false) String id, Model model) {
model.addAttribute("id", id);
return "index";
}
@GetMapping("/logIn")
public String logIn() {
return "logIn";
}
@PostMapping("/logIn")
public String logInPost(@RequestParam String id, HttpSession session, @RequestParam(defaultValue = "/") String redirectURI) {
session.setAttribute("id", id);
return "redirect:" + redirectURI;
}
@GetMapping("/info1")
public String info1(Model model) {
return "info1";
}
@GetMapping("/info2")
public String info2(Model model) {
return "info2";
}
}
메인 페이지에서 로그인 페이지와 info1, info2페이지로 이동할 수 있다.
이 예제에서는 로그인을 저장소와 비교하지 않고, 간략하게 그저 폼에 입력하는대로 세션에 그대로 저장했다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<title>메인페이지</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<ul>
<li th:unless="${id}"><a href="/logIn" th:text="로그인">로그인</a></li>
<li th:if="${id}"><a th:text="${id}+'님 어서오세요~'">김철수님 어서오세요~</a></li>
<li><a href="/info1">로그인 상태만 접근 가능한 정보 페이지1</a></li>
<li><a href="/info2">로그인 상태만 접근 가능한 정보 페이지2</a></li>
</ul>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<title>로그인</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<h1>로그인</h1>
<form method="post">
<div class="mb-3 mt-3">
<label class="form-label" for="id">id:</label>
<input class="form-control" type="text" id="id" name="id">
</div>
<button type="submit" class="btn btn-primary">확인</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
logIn.html
HttpSession session으로 session.setAttribute로 세션 속성을 만들어줬고, @SessionAttribute(name="id", required = false) String id로 세션 속성값을 읽었다. 이때 required를 false로 두면 로그인하지 않은 유저에게는 세션을 생성하지 않는다.
2. 인터셉터
그렇다면 로그인 상태에서만 접근 가능하게 만들고싶은 페이지가 있을 것이다. 글 작성이라던가 정보 페이지 말이다. 이것은 스프링 인터셉터 기능을 이용해 구현하면 된다.
스프링 인터셉터를 구현하려면 HandlerInterceptor를 구현하는 클래스를 만들고 WebMvcConfigurer를 구현하는 클래스에 인터셉터 등록을 해야한다.
public class LogInInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if(session == null || session.getAttribute("id") == null) {
response.sendRedirect("/logIn?redirectURI=" + request.getRequestURI());
return false;
}
return true;
}
}
LogInInterceptor.java (interceptor패키지)
컨트롤러가 호출되기 전에 세션값을 읽어 세션값이 없으면 로그인 페이지로 redirect하는 코드이다. return값이 true이면 컨트롤러가 호출되고 false이면 컨트롤러가 호출되지 않는다.
HandlerInterceptor에는 preHandle, postHandle, afterCompletion 메소드가 있는데, 다음과 같은 순서로 실행된다.
그러므로 로그인하지 않은 유저에게 서비스를 거부할 것이라면 preHandle만 구현하면 된다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInInterceptor())
.addPathPatterns("/**").excludePathPatterns("/", "/logIn");
}
}
WebConfig.java (@SpringBootApplication와 같은 패키지)
WebMvcConfigurer의 addInterceptors를 오버라이드해 registry에 내가 만든 인터셉터를 추가한다. addPathPatterns로 인터셉터를 적용할 패턴들을 등록하고, excludePathPatterns로 제외할 패턴들을 등록하면 된다.
'Java' 카테고리의 다른 글
spring 예외(Exception) 처리 (0) | 2025.04.17 |
---|---|
spring 검증(validation, bean validation) (0) | 2025.04.11 |
spring 단위 테스트 코드 작성 (0) | 2025.03.11 |
spring 기본 개념 (0) | 2025.03.11 |
thymeleaf 템플릿 엔진 (0) | 2025.02.11 |