본문 바로가기
Java/Servlet & JSP

JSP) Filter - 생성, 생명주기, 중복코드/접근권한 제어하기

by 박채니 2022. 6. 26.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


Filter

- HTTP 요청과 응답 사이에서 전달되는 데이터를 가로채어 서비스에 맞게 변경하고 걸러내는 필터링 작업을 수행

- 요청한 URL을 기준으로 필터링 작업

- servlet이 실행되기 전에 먼저 수행 (전처리) / 후처리도 가능 (응답 메세지를 모두 작성하고 마지막에 client에게 보내기 전에 수행)

- filter는 여러개 존재 가능 → filter chain

filter를 통함!


filter 생성

filter package 생성 - Next

 

Filter mappings (어떤 URL에 filter 적용을 할 건지 적용) → *는 모든 URL을 의미

 

Filter의 생명주기

① 객체생성 (생성자 호출)

② init (필터객체 전처리)

③ doFilter (처리코드)

④ destroy (메모리 반환 전 처리)

/**
 * Filter의 생명주기
 * - 객체생성 (생성자 호출)
 * - init (필터객체 전처리)
 * - doFilter (처리코드)
 * - destroy (메모리 반환 전 처리)
 */
@WebFilter("/*")
public class LogFilter implements Filter {

    /**
     * Default constructor. 
     */
    public LogFilter() {
        System.out.println("[LogFilter 생성]");
    }

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		System.out.println("[LogFilter destroy]");
	}

	/**
	 * 필터를 통해 처리할 코드 작성
	 * - 전처리
	 * - 후처리
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 전처리
        
		// filter chain의 다음 filter를 호출 (마지막 필터라면 servlet 호출)
		chain.doFilter(request, response);
        
		// 후처리
	}

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("[LogFilter init]");
	}
}

위와 같이 클래스가 생성되었습니다.

 

실제 처리 코드를 작성하는 doFilter메소드를 자세히 확인해보도록 하겠습니다.

doFilter의 매개변수를 확인해보면 ServletRequest, ServletResponse인 것을 확인할 수 있습니다.

Servlet에서의 doGet, doPost메소드에서는 HttpServletRequest/Response 타입이였지만, Filter는 부모 인터페이스인 ServletRequest/Response 타입을 제어하고 있는 것을 알 수 있습니다.

즉, request/response에는 같은 객체가 넘어오지만 제어하고 있는 타입이 다르다는 것!! (다형성)

- FilterChain은 filte들을 관리하고 있는 객체 (다음 filter를 호출하기 위해 매개인자로 넘겨줌)

chain.doFilter(request, response) 로 filter chain상의 다음 filter를 호출하며, 마지막 필터라면 servlet을 호출합니다.

 

@WebFilter("/*")
public class LogFilter implements Filter {

    /**
     * Default constructor. 
     */
    public LogFilter() {
        System.out.println("[LogFilter 생성]");
    }

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		System.out.println("[LogFilter destroy]");
	}

	/**
	 * 필터를 통해 처리할 코드 작성
	 * - 전처리
	 * - 후처리
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 전처리
		HttpServletRequest httpReq = (HttpServletRequest) request;
		String uri = httpReq.getRequestURI();
		String method = httpReq.getMethod();
		
		System.out.println("==========================================");
		System.out.printf("%s %s\n", method, uri);
		System.out.println("------------------------------------------");
		
		// filter chain의 다음 filter를 호출 (마지막 필터라면 servlet 호출)
		chain.doFilter(request, response);
		
		// 후처리
		HttpServletResponse httpRes = (HttpServletResponse) response;
		System.out.println(httpRes.getStatus());
		System.out.println("------------------------------------------");
		System.out.println();
	}

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("[LogFilter init]");
	}
}

@콘솔출력값
[LogFilter 생성]
[LogFilter init]
==========================================
GET /mvc2/
------------------------------------------
[sessionCreated] 현재 세션 수 : 1
200
------------------------------------------

==========================================
GET /mvc2/css/style.css
------------------------------------------
304
------------------------------------------

==========================================
POST /mvc2/member/login
------------------------------------------
[회원로그인] 홍길동님 로그인!
302
------------------------------------------

==========================================
GET /mvc2/
------------------------------------------
JSESSIONID = 164EBB6FDB46A44AE0E95785930D8DE5
200
------------------------------------------

[LogFilter destroy]
[회원로그아웃] 홍길동님 로그아웃!

getRequestURI, getMethod, getStatus는 HttpServletRequest객체에서 사용 가능하므로, 다운캐스팅을 해주었습니다.

 


중복되는 코드 filter로 처리하기

Controller단의 중복코드인 인코딩 처리를 filter로 해결!

@WebFilter("/*")
public class EncodingFilter implements Filter {
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		String encodingType = "utf-8";
		request.setCharacterEncoding(encodingType);
		
		System.out.println("[EncodingFilter : " + encodingType + " 처리]");
		
		chain.doFilter(request, response);
	}
}

@콘솔출력값
[LogFilter 생성]
[LogFilter init]

[EncodingFilter : utf-8 처리]
==========================================
GET /mvc2/
------------------------------------------
JSESSIONID = 0B6E0F8C0B614BC5FF77F705D3CC4507
200
------------------------------------------

출력값을 보면 EncodingFilter가 먼저 처리되고, 위에서 생성했던 LogFilter가 처리되는 것을 확인할 수 있습니다.

어노테이션으로 작성한 Filter의 처리순서는 알파벳 순이므로, 순서를 지정하고자 한다면, web.xml에 등록해야합니다.

 

web.xml

  <filter>
  	<filter-name>LogFilter</filter-name>
  	<filter-class>com.ce.mvc2.common.filter.LogFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>LogFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter>
  	<filter-name>EncodingFilter</filter-name>
  	<filter-class>com.ce.mvc2.common.filter.EncodingFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>EncodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
@콘솔출력값
[LogFilter 생성]
[LogFilter init]

==========================================
GET /mvc2/
------------------------------------------
[EncodingFilter : utf-8 처리]
JSESSIONID = 0B6E0F8C0B614BC5FF77F705D3CC4507
200
------------------------------------------

이번엔 LogFilter가 먼저 처리된 후 EncodingFilter가 처리되는 것을 확인할 수 있습니다.

(web.xml에 등록된 filter가 먼저 처리되고, 어노테이션으로 등록한 filter가 처리!)

 

web.xml에서 init 파라미터 작성하여 처리도 가능

  <filter>
  	<filter-name>EncodingFilter</filter-name>
  	<filter-class>com.ce.mvc2.common.filter.EncodingFilter</filter-class>
  	<init-param>
  		<param-name>encodingType</param-name>
  		<param-value>utf-8</param-value>
  	</init-param>
  </filter>
public class EncodingFilter implements Filter {
	private String encodingType;
    
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//		String encodingType = "utf-8";
		request.setCharacterEncoding(encodingType);
		
		System.out.println("[EncodingFilter : " + encodingType + " 처리]");
		
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
		this.encodingType = fConfig.getInitParameter("encodingType");
	}
}

@콘솔출력값
[LogFilter 생성]
[LogFilter init]

==========================================
GET /mvc2/
------------------------------------------
[EncodingFilter : utf-8 처리]
JSESSIONID = 0B6E0F8C0B614BC5FF77F705D3CC4507
200
------------------------------------------

 


접근권한을 Filter로 통제하기

로그인을 해야만 이용할 수 있는 페이지에 직접 URL을 작성하여 접근하였을 때를 대비하여 Filter로 처리해보겠습니다.

내정보보기(memberView)는 loginMember를 통해서 id, name 등 정보를 가져오지만, 현재 로그인이 되어있지 않은 상태에서 접근하였기 때문에 아래와 같은 NullPointerException이 발생하는 것을 확인할 수 있습니다.

 

LoginFilter

//@WebFilter(urlPatterns = { "/member/memberView", "/member/memberUpdate", "/member/memberDelete" })
//@WebFilter(value = { "/member/memberView", "/member/memberUpdate", "/member/memberDelete" })
@WebFilter({ "/member/memberView", "/member/memberUpdate", "/member/memberDelete" })
public class LoginFilter implements Filter {
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpReq = (HttpServletRequest) request;
		HttpServletResponse httpRes = (HttpServletResponse) response;
		HttpSession session = httpReq.getSession();
		Member loginMember = (Member) session.getAttribute("loginMember");
		
		if(loginMember == null) {
			session.setAttribute("msg", "로그인 후 이용 가능합니다.");
			httpRes.sendRedirect(httpReq.getContextPath() + "/");
			return;
		}
		
		chain.doFilter(request, response);
	}
}

@WebFilter 어노테이션을 통해 적용할 URL을 지정해주었습니다.

(urlPatterns, value를 key값으로 갖지만 생략가능!)

 

index페이지로 redirect 처리하였으므로, memberView는 302, mvc2/는 200 상태인 것을 확인할 수 있습니다.

또한, return을 하여 servlet이 실행되지 않도록 하였습니다. (return 하지 않으면 안됨!)