다만 필터 순서 조절이 안 되니 그냥 FilterRegistrationBean을 사용하자.
서블릿 필터 - 인증 체크
로그인 여부를 확인하는 필터를 만들고 적용해보자.
필터 만들기
packagehello.login.web.filter;importhello.login.web.SessionConst;importjakarta.servlet.*;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjakarta.servlet.http.HttpSession;importlombok.extern.slf4j.Slf4j;importorg.springframework.util.PatternMatchUtils;importjava.io.IOException;@Slf4jpublicclassLoginCheckFilterimplementsFilter{privatestaticfinalString[]whitelist={"/","/members/add","/login","/logout","/css/*"};@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequesthttpRequest=(HttpServletRequest)request;StringrequestURI=httpRequest.getRequestURI();HttpServletResponsehttpResponse=(HttpServletResponse)response;try{log.info("인증 체크 필터 시작 {}",requestURI);if(isLoginCheckPath(requestURI)){log.info("인증 체크 로직 실행 {}",requestURI);HttpSessionsession=httpRequest.getSession(false);if(session==null||session.getAttribute("loginMember")==null){log.info("미인증 사용자 요청 {}",requestURI);//로그인으로 redirecthttpResponse.sendRedirect("/login?redirectURL="+requestURI);return;//여기가 중요, 미인증 사용자는 다음으로 진행하지 않고 끝!}}chain.doFilter(request,response);}catch(Exceptione){throwe;//예외 로깅 가능 하지만, 톰캣까지 예외를 보내주어야 함}finally{log.info("인증 체크 필터 종료 {}",requestURI);}}/**
* 화이트 리스트의 경우 인증 체크X
*/privatebooleanisLoginCheckPath(StringrequestURI){return!PatternMatchUtils.simpleMatch(whitelist,requestURI);}}
필터 적용하기
WebConfig에 추가해주자.
@BeanpublicFilterRegistrationBeanloginCheckFilter(){FilterRegistrationBean<Filter>filterRegistrationBean=newFilterRegistrationBean<>();filterRegistrationBean.setFilter(newLoginCheckFilter());//필터 종류 지정filterRegistrationBean.setOrder(2);//순번 지정filterRegistrationBean.addUrlPatterns("/*");//적용할 URL 패턴 지정returnfilterRegistrationBean;}
예를 들어서 로그를 남기는 인터셉터를 먼저 적용하고, 그 다음에 로그인 여부를 체크하는 인터셉터를 만들 수 있다.
스프링 인터셉터 인터페이스
스프링의 인터셉터를 사용하려면 HandlerInterceptor 인터페이스를 구현하면 된다.
preHandle
컨트롤러 호출 전에 호출된다. (더 정확히는 핸들러 어댑터 호출 전에 호출된다.)
preHandle의 응답값이 true면 다음으로 진행하고, false 이면 더는 진행하지 않는다.
false인경우 나머지 인터셉터는 물론이고, 핸들러 어댑터도 호출되지 않는다.
postHandle
컨트롤러 호출 후에 호출된다. (더 정확히는 핸들러 어댑터 호출 후에 호출된다.)
afterCompletion
뷰가 렌더링 된 이후에 호출된다.
예외가 발생한 경우에도 항상 호출된다.
스프링 인터셉터 - 요청 로그
로그를 출력하는 인터셉터를 만들어보자.
인터셉터 만들기
packagehello.login.web.interceptor;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importjava.util.UUID;@Slf4jpublicclassLogInterceptorimplementsHandlerInterceptor{publicstaticfinalStringLOG_ID="logId";@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{StringrequestURI=request.getRequestURI();Stringuuid=UUID.randomUUID().toString();request.setAttribute(LOG_ID,uuid);//@RequestMapping: HandlerMethod//정적 리소스: ResourceHttpRequestHandlerif(handlerinstanceofHandlerMethod){HandlerMethodhm=(HandlerMethod)handler;//호출할 컨트롤러 메서드의 모든 정보가 포함되어 있다.}log.info("REQUEST [{}][{}][{}]",uuid,requestURI,handler);returntrue;//false 진행X}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{log.info("postHandle [{}]",modelAndView);}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{StringrequestURI=request.getRequestURI();StringlogId=(String)request.getAttribute(LOG_ID);log.info("RESPONSE [{}][{}]",logId,requestURI);if(ex!=null){log.error("afterCompletion error!!",ex);}}}
packagehello.login.web.interceptor;importhello.login.web.SessionConst;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importjakarta.servlet.http.HttpSession;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.servlet.HandlerInterceptor;@Slf4jpublicclassLoginCheckInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{StringrequestURI=request.getRequestURI();log.info("인증 체크 인터셉터 실행 {}",requestURI);HttpSessionsession=request.getSession(false);if(session==null||session.getAttribute("loginMember")==null){log.info("미인증 사용자 요청");//로그인으로 redirectresponse.sendRedirect("/login?redirectURL="+requestURI);returnfalse;}returntrue;}}
인터셉터 적용하기
WebConfig의 addInterceptors에 추가해주자.
registry.addInterceptor(newLoginCheckInterceptor()).order(2)//순번 지정.addPathPatterns("/**")//적용할 URL 패턴 지정.excludePathPatterns("/","/members/add","/login","/logout","/css/**","/*.ico","/error");//예외 URL 패턴 지정
ArgumentResolver 활용
이번에는 ArgumentResolver를 통해서 로그인한 회원을 조금 편리하게 찾는 방법을 알아보자.
전용 애노테이션을 생성해서 리졸버를 통해 해당 애노테이션이 존재하면 로그인한 회원 정보를 반환하는 방법이다.