BE/Spring
스프링 MVC 2 - 서블릿 필터 인증 체크
오봉봉이
2022. 8. 29. 21:55
728x90
서블릿 필터 - 인증 체크
인증 체크 필터를 개발해보자.
로그인 되지 않은 사용자는 상품 관리 뿐만 아니라 미래에 개발될 페이지에도 접근하지 못하도록 하자.
LoginCheckFilter - 인증 체크 필터
package hello.login.web.filter;
import hello.login.web.SessionConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.PatternMatchUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@Slf4j
public class LogCheckFilter 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;
try {
if (isLoginCheckPath(requestURI)) {
log.info("인증 체크 로직 실행 {}", requestURI);
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
log.info("미인증 사용자 요청 {}", requestURI);
// 로그인으로 redirect
httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
return; //여기가 중요, 미인증 사용자는 다음으로 진행하지 않고 끝!
}
}
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
log.info("인증 체크 필터 종료 {}", requestURI);
}
}
/**
* 화이트 리스트의 경우 인증 체크X
*/
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
}
}
- whitelist = {"/", "/members/add", "/login", "/logout","/css/*"};
- 인증 필터를 적용해도 홈, 회원가입, 로그인 화면, css 같은 리소스에는 접근할 수 있어야 한다.
- 화이트 리스트 경로는 인증과 무관하게 항상 허용한다.
- 화이트 리스트를 제외한 나머지 모든 경로에는 인증 체크 로직을 적용한다.
- isLoginCheckPath(requestURI)
- 화이트 리스트를 제외한 모든 경우에 인증 체크 로직을 적용한다.
- httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
- 미인증 사용자는 로그인 화면으로 리다이렉트 하는데, 로그인 이후에 다시 홈으로 이동하면 원하는 경로를 다시 찾아가야 하는 불편함이 있다.
- 예 : 상품 관리 화면 요청 -> 로그인 하지 않아서 필터에 걸림 -> 로그인 화면 -> 로그인 -> 홈
- 해결 : 상품 관리 화면 요청 -> 로그인 하지 않아서 필터에 걸림 -> 로그인 화면 -> 로그인 -> 상품 관리 화면
- 이런 기능을 위해 현재 요청한 경로인 requestURI를 /login에 쿼리 파라미터로 함께 전달한다.
- 물론 로그인 성공시 해당 경로로 이동하는 기능은 추가로 개발해야 한다.
- 미인증 사용자는 로그인 화면으로 리다이렉트 하는데, 로그인 이후에 다시 홈으로 이동하면 원하는 경로를 다시 찾아가야 하는 불편함이 있다.
- return;
- 여기가 중요한데, 필터를 더는 진행하지 않는다.
- 이후 필터는 물론 서블릿, 컨트롤러가 더는 호출되지 않는다.
- 앞서 redirect를 사용했기 때문에 redirect가 응답으로 적용되고 요청이 끝난다.
WebConfig - loginCheckFilter() 추가
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
filterFilterRegistrationBean.setFilter(new LogCheckFilter());
filterFilterRegistrationBean.setOrder(2);
filterFilterRegistrationBean.addUrlPatterns("/*");
return filterFilterRegistrationBean;
}
}
- setFilter(new LoginCheckFilter()) : 로그인 필터를 등록한다.
- setOrder(2) : 순서를 2번으로 잡았다. 로그 필터 다음에 로그인 필터가 적용된다.
- addUrlPatterns("/*") : 모든 요청에 로그인 필터를 적용한다.
LoginController - loginV4()
@PostMapping("/login")
public String loginV4(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult,
@RequestParam(defaultValue = "/") String redirectURL, HttpServletRequest request) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
log.info("login? {}", loginMember);
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
// 로그인 성공 처리
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:" + redirectURL;
}
- @RequestParam(defaultValue = "/") String redirectURL
- defaultValue = "/"를 통해 값이 없으면 /로 설정했다.
- "redirect:" + /;가 호출됨.
- 로그인 체크 필터에서, 미인증 사용자는 요청 경로를 포함해서
/login
에redirectURL
요청 파라미터를 추가해서 요청했다. - 이 값을 사용해서 로그인 성공시 해당 경로로 고객을
redirect
한다.
- defaultValue = "/"를 통해 값이 없으면 /로 설정했다.
정리
서블릿 필터를 사용해서 로그인 하지 않은 사용자는 나머지 경로에 들어갈 수 없게 되었다.
공통 관심사를 서블릿 필터를 사용해서 해결했기 때문에 향후 로그인 관련 정책이 변경되어도 이 부분만 변경하면 된다.
참고
필터에는 스프링 인터셉터는 제공하지 않는, 아주 강력한 기능이 있는데 chain.doFilter(request, response);
를 호출해서 다음 필터 또는 서블릿을 호출할 때 request
, response
를 다른 객체로 바꿀 수 있다.ServletRequest
, ServletResponse
를 구현한 다른 객체를 만들어서 넘기면 해당 객체가 다음 필터 또는 서블릿에서 사용된다.
잘 사용하는 기능은 아니니 필요하면 검색을 해보자.
출처 : 인프런 김영한 지식공유자님 강의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
728x90