프로젝트 설정
application.properties
#port
server.port=9090
#thymeleaf cache
spring.thymeleaf.cache=false
#encoding
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
server.servlet.encoding.enabled=true
# 타임리프에서 url을 통해 세션을 유지하는 jsessionid가 생성되는 것을 방지
server.servlet.session.tracking-modes=cookie
시작
LoginController.java
수정@Controller
@RequiredArgsConstructor
public class LoginController {
@PostMapping("/login")
public String login(@ModelAttribute LoginForm form, Model model, RedirectAttributes redirectAttributes , HttpServletResponse response) {
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if( loginMember == null) {
// 로그인실패
model.addAttribute("msg", "로그인실패");
return "login/loginForm";
}
// 로그인 성공
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
redirectAttributes.addFlashAttribute("msg","로그인 성공");
return "redirect:/";
}
}
HomeController.java
수정@Controller
@RequiredArgsConstructor
public class HomeController {
private final MemberRepository memberRepository;
...
...
@GetMapping
public String homev2(@CookieValue(name = "memberId", required = false)Long memberId, Model model) {
// 로그인한 사용자가 아니라면 home으로 보낸다.
if ( memberId == null) {
return "home";
}
// db 조회
Member loginMember = memberRepository.findById(memberId);
// 사용자가 없으면 null 처리 필요
if(loginMember == null) {
return "home";
}
// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
model.addAttribute("member", loginMember);
return "loginHome";
}
}
loginHome.html
생성<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<title>Insert title here</title>
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>홈 화면</h2>
</div>
<h4 class="mb-3" th:text="|로그인 : ${member.name}|">로그인 사용자 이름</h4>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-secondary btn-lg" type="button" th:onclick="|location.href='@{/items}'|">상품 관리</button>
</div>
<div class="col">
<form method="post" th:action="@{/logout}">
<button class="w-100 btn btn-dark btn-lg" onclick="location.href='items.html'" type="submit"> 로그아웃 </button>
</form>
</div>
</div>
<hr class="my-4">
</div>
<!-- /container -->
</body>
</html>
LoginController.java
수정...
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
Cookie cookie = new Cookie("memberId", null);
cookie.setMaxAge(0);
response.addCookie(cookie);
return "redirect:/";
}
...
SessionConst.java
생성public class SessionConst {
public static final String LOGIN_MEMBER = "loginMember";
}
LoginController.java
수정...
@PostMapping("/login") // 세션에 담아주기
public String loginv2(@ModelAttribute LoginForm form, Model model, RedirectAttributes redirectAttributes , HttpServletRequest request) {
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
System.out.println(loginMember);
if( loginMember == null) {
// 로그인실패
model.addAttribute("msg", "로그인실패");
return "login/loginForm";
}
// 로그인 성공
HttpSession session = request.getSession();
//세션에 로그인 회원정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
redirectAttributes.addFlashAttribute("msg","로그인 성공");
return "redirect:/";
}
...
@PostMapping("/logout")
public String logoutv2(HttpServletRequest request) {
//세션을 삭제
HttpSession session = request.getSession(false);
if(session != null) {
session.invalidate();
}
return "redirect:/";
}
}
HomeController.java
...
@GetMapping
public String homev3(HttpServletRequest request, Model model) {
HttpSession session = request.getSession(false);
// 로그인한 사용자가 아니라면 home으로 보낸다.
if ( session == null) {
return "home";
}
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
// 사용자가 없으면 null 처리 필요
if(loginMember == null) {
return "home";
}
// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
model.addAttribute("member", loginMember);
return "loginHome";
}
}
결과 > 브라우저 내에서 로그인 유지
HomeController.java
...
@GetMapping
public String homev4(@SessionAttribute(name=SessionConst.LOGIN_MEMBER, required= false)Member loginMember , Model model) {
// 사용자가 없으면 null 처리 필요
if(loginMember == null) {
return "home";
}
// loginHome : 로그인에 성공한 사람만이 볼 수 있는 화면
model.addAttribute("member", loginMember);
return "loginHome";
}
}
application.properties
수정server.servlet.session.timeout=시간
초기 세팅
ItemController.java
수정@Controller
@RequestMapping("/items")
@RequiredArgsConstructor
public class ItemController {
...
...
}
TestDataInit.java
수정
@Component
@RequiredArgsConstructor
public class TestDataInit {
...
@PostConstruct
public void init() {
// 테스트 데이터 추가
itemRepository.save(new Item("testA", 10000, 10));
itemRepository.save(new Item("testB", 20000, 20));
...
}
}
컨트롤러에 일일이 조건을 걸어줄 필요 없이, 필터를 사용해서 모든 url에 적용하기
LogFilter.java
생성import javax.servlet.Filter;
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
String requestURI = httpServletRequest.getRequestURI();
System.out.println("requestURI : " + requestURI); // 필터 전 url값
chain.doFilter(request, response);
System.out.println("responseURI : " + requestURI); // 필터 후 url 값
}
}
WebConfig.java
생성@Component
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
filterRegistrationBean.setFilter(new LogFilter()); // LogFilter 등록
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*"); // 모든 url 다 적용
return filterRegistrationBean;
}
}
필터를 사용해서 로그인해야만 메뉴가 보이게 설정
loginweb.filter 패키지 > LoginCheckFilter.java
생성
public class LoginCheckFilter implements Filter{
private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
String requestURI = httpRequest.getRequestURI();
HttpServletResponse httpResponse = (HttpServletResponse)response;
System.out.println("인증 체크 필터 시작");
if(isLoginCheckPath(requestURI)) {
System.out.println("인증 체크 로직 실행 : " + requestURI);
HttpSession session = httpRequest.getSession(false);
if(session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
System.out.println("미 인증 사용자 요청");
httpResponse.sendRedirect("/login");
return; // 미인증 사용자는 다음으로 진행하지 않고 끝낸다.
}
}
/* 다음 단계로 넘어간다. */
chain.doFilter(request, response);
}
/*
whitelist에 해당하는 url인 경우 인증 체크 x
simpleMatch : 파라미터 문자열이 특정 패턴에 매칭되는지를 검사함.
*/
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
WebConfig.java
수정...
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
filterRegistrationBean.setFilter(new LoginCheckFilter());
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
loginCheckFilter.java
수정public class LoginCheckFilter implements Filter{
private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
if(isLoginCheckPath(requestURI)) {
System.out.println("인증 체크 로직 실행 : " + requestURI);
HttpSession session = httpRequest.getSession(false);
if(session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
System.out.println("미 인증 사용자 요청");
httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
return; // 미인증 사용자는 다음으로 진행하지 않고 끝낸다.
}
}
...
}
...
@PostMapping("/login") // 세션에 담아주기
public String loginv3(@ModelAttribute LoginForm form, Model model, RedirectAttributes redirectAttributes , HttpServletRequest request,
@RequestParam(defaultValue = "/")String redirectURL ) {
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
System.out.println(loginMember);
if( loginMember == null) {
// 로그인실패
model.addAttribute("msg", "로그인실패");
return "login/loginForm";
}
// 로그인 성공
HttpSession session = request.getSession();
//세션에 로그인 회원정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
redirectAttributes.addFlashAttribute("msg","로그인 성공");
return "redirect:" + redirectURL;
}
...
- 스프링 인터셉터도 서블릿 필터와 같이 웹과 관련된 공통 관심 사항을 효과적으로 해결 할 수 있는 기술이다. 서블릿 필터가 서블릿이 제공하는 기술이라면, 스프링 인터셉터는 스프링 MVC가 제공하는 기술이다.
- 둘다 웹과 관련된 공통 관심 사항을 처리하지만, 적용되는 순서와 범위, 사용방법이 다르다.
- HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 - > 컨트롤러
- 스프링 인터셉터는 디스패처서블릿과 컨트롤러 사이에서 컨트롤러 호출 직전에 호출된다.
- 스프링 인터셉터는 스프링 MVC가 제공하는 기능이기 때문에 결국 디스패처 서플릿 이후에 등장하게 된다.
- 정밀한 URL패턴을 적용 할 수 있다.
- HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 - > 컨트롤러
- HandlerInerceptor 인터페이스 사용
- 컨트롤러 호출 전 : preHandle
- 컨트롤러 호출 후 : postHandle
- 요청 완료 이후 : afterCompletion, 뷰가 렌더링 된 이후에 호출된다.
LogInterceptor.java
생성public class LogInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestURI = request.getRequestURI();
System.out.println("[interceptor] requestURI : " + requestURI);
return true; // false -> 진행 x
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[interceptor] postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[interceptor] afterCpmpletion");
}
}
WebConfig.java
수정@Component
public class WebConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/error");
}
}
LoginCheckInterceptor.java
생성public class LoginCheckInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestURI = request.getRequestURI();
System.out.println("[interceptor] : " + requestURI);
HttpSession session = request.getSession(false);
if( session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
System.out.println("[미인증 사용자 요청]");
// 로그인으로 redirect
response.sendRedirect("/login?redirectURL=" + requestURI);
return false;
}
return true;
}
}
WebConfig.java
수정@Component
public class WebConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( new LogInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/error");
registry.addInterceptor( new LoginCheckInterceptor())
.order(2)
.addPathPatterns("/**")
.excludePathPatterns("/", "/members/add", "/login", "/logout", "/css/**");
}
}