3 분 소요

 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술편을 학습하고 정리한 내용 입니다.

서블릿 필터 - 인증 체크

로그인 되지 않은 사용자는 상품 관리 뿐만이 아니라 미래에 개발될 페이지에도 접근하지 못하도록 하자.

인증 체크 필터 구현

hello.login.web.filter.LoginCheckFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Slf4j  
public class LoginCheckFilter implements Filter {  
  
    private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};  
  
    @Override  
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {  
  
        HttpServletRequest request = (HttpServletRequest) servletRequest;  
        String requestURI = request.getRequestURI();  
  
        HttpServletResponse response = (HttpServletResponse) servletResponse;  
  
  
        try {  
            log.info("인증 체크 필터 시작 {}", requestURI);  
            if (isLoginCheckPath(requestURI)) {  
                log.info("인증 체크 로직 실행 {}", requestURI);  
                HttpSession session = request.getSession();  
                if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {  
                    log.info("미인증 사용자 요청 {}", requestURI);  
                    // 로그인페이지로 redirect
                    response.sendRedirect("/login?redirectURL="+requestURI);  
                    return;  
                }  
            }  
            filterChain.doFilter(servletRequest, servletResponse);  
  
        } catch (Exception e) {  
            throw e;  
        } finally {  
            log.info("인증 체크 로직 종료 {}", requestURI);  
        }  
  
    }  
  
    /**  
     * 화이트리스트인 경우 인증 체크 X  
     * */    
    private boolean isLoginCheckPath(String requestURI) {  
        return !PatternMatchUtils.simpleMatch(whitelist, requestURI);  
    }  
  
}

먼저 필터를 만들어 줬다. 여기서 또 처음 알았던 점이 implements 하면서 default 메서드는 구현 하지 않아도 된다는 점이다.

그래서 이번엔 doFilter()만 구현했다.

1
private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};

필터를 적용해도 홈, 회원 가입, 로그인 화면, css 같은 리소스에는 접근 가능해야 한다.

그래서 이 url 접근이 오면 인증을 생략하게 하는 로직을 넣어야 한다.

1
2
3
private boolean isLoginCheckPath(String requestURI) {  
	return !PatternMatchUtils.simpleMatch(whitelist, requestURI);  
} 

스프링이 제공하는 유틸성 클래스 PatternMatchUtils를 이용해 화이트 리스트에 있는 문자열과, 사용자가 접근하는 url에

겹치는 부분이 있으면 false, 겹치는 부분이 없으면 true를 반환하도록 설계

  • simpleMatch() : 어떤 문자열이 특정 패턴에 매칭 되는지를 검사함.
1
2
3
4
5
6
7
8
9
10
if (isLoginCheckPath(requestURI)) {  
	log.info("인증 체크 로직 실행 {}", requestURI);  
	HttpSession session = request.getSession();  
	if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {  
		log.info("미인증 사용자 요청 {}", requestURI);  
		// 로그인페이지로 redirect
		response.sendRedirect("/login?redirectURL="+requestURI);  
		return;  
	}  
}  

이제 isLoginCheckPath()로 화이트 리스트를 체크하고

세션을 확인하고, 세션이 있는데 로그인 관련된 세션이 있는지 확인하고

없으면

1
response.sendRedirect("/login?redirectURL="+requestURI);

로그인 페이지로 리다이렉트 시키는 로직이다.

그런데 여기서 ?redirectURL="+requestURI 가 있는데

이는 로그인 후에 사용자가 원래 접근 하려던 페이지로 다시 보내주기 위함이다.

1
filterChain.doFilter(servletRequest, servletResponse);

로그인 필터에 안 걸렸다면 이제 다음 필터로 넘어가도록 선언해 주면 끝난다.

인증 체크 필터 등록

hello.login.WebConfig

1
2
3
4
5
6
7
8
@Bean  
public FilterRegistrationBean loginCheckFilter() {  
    FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();  
    filterRegistrationBean.setFilter(new LoginCheckFilter());  
    filterRegistrationBean.setOrder(2); // 필터 순서  
    filterRegistrationBean.addUrlPatterns("/*"); // 모든 URL  
    return filterRegistrationBean;  
}

저번에 로그 필터를 등록 한 것처럼 FilterRegistrationBean을 사용해 등록했다.

필터 순서를 로그 필터 이후로 등록하기 위해 2로 등록했다.

로그인 컨트롤러 리다이렉트 처리

마지막으로 ?redirectURL="+requestURI 이 요청을 처리하기 위해

로그인 컨트롤러를 약간 수정하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@PostMapping("/login")  
public String loginV4(@Validated @ModelAttribute("loginForm") LoginForm form, BindingResult result,  
                      @RequestParam(defaultValue = "/") String redirectURL,  
                      HttpServletRequest request) {  
  
    if (result.hasErrors()) {  
        return "login/loginForm";  
    }  
  
    Member loginMember = loginService.login(form.getLoginId(), form.getPassword());  
  
    if (loginMember == null) {  
        result.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");  
        return "login/loginForm";  
    }  
  
    // 로그인 성공 처리  
    // 세션이 있으면 있는 세션 반환, 없으면 신규 세션을 생성  
    HttpSession session = request.getSession();  
    // 세션에 로그인 회원 정보를 보관  
    session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);  
  
    return "redirect:" + redirectURL;  
}

먼저 파라미터에

1
@RequestParam(defaultValue = "/") String redirectURL

다음과 같이 ?redirectURL="+requestURI를 받아주기 위해 @RequestParam을 사용했고,

그냥 로그인 페이지에서 로그인 한 것 일수도 있기 때문에 defaultValue = "/"을 넣어줬다.

그다음엔

1
return "redirect:" + redirectURL;

이렇게 리다이렉트 시키면 끝이다.

이제 결과를 보자.

메인 페이지에서 /items로 직접 접근했을 때 로그인 페이지로 가버리고 ?redirectURL=/items가 생겼다.

로그를 보면

로그 필터 -> 인증 체크 필터 -> 인증 체크 파이널 -> 로그 파이널 이 순서로 실행 된 걸 볼 수 있다.

로그인 해주면 바로 items로 이동하게 됐다.

공통 관심사를 서블릿 필터를 사용해서 해결한 덕분에 향후 로그인 관련 정책이 변경되어도 이 부분만 변경하면 된다.

댓글남기기