728x90
반응형
SMALL

이번에는 서블릿 필터를 만들어보자. 서블릿 필터는 사실 그냥 Java로 서블릿을 사용하면 거의 무조건 사용하는 컴포넌트이다.

그래서 이건 뭐 JIRA 플러그인을 개발하기 위해 따로 알아야 하는 개념이 아니라 아마 익숙할 것 같다.

 

우선 서블릿 필터를 만드려면 당연히 dependencies로 서블릿이 있어야 할 것이고, 이건 이미 이 전 포스팅에서 다뤘다.

그리고 필터를 구현하는 클래스를 만들면 된다.

 

CustomServletFilter

package kr.osci.kapproval.admin.servlet.filter;

import lombok.RequiredArgsConstructor;
import javax.servlet.*;
import java.io.IOException;

@RequiredArgsConstructor
public class CustomServletFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 
    }

    @Override
    public void destroy() {

    }
}
  • void init(): 필터 초기화 작업이 필요하다면 이 메서드에 작성하면 된다.
  • doFilter(): 각 필터마다 이 메서드에서 필터 처리를 한다. 필터는 요청을 다음 필터 또는 서블릿으로 전달할지 여부를 결정할 수 있습니다.
  • destroy(): 필터 종료 작업이 필요하다면 이 메서드에 작성하면 된다.

필터의 주요 역할 

  • 요청(Request)에 대한 전처리: 클라이언트 요청이 서블릿이나 JSP로 전달되기 전에 요청을 수정하거나, 인증/인가 검사를 수행하거나, 로깅 등을 할 수 있다.
  • 응답(Response)에 대한 후처리: 서블릿이나 JSP가 응답을 만들고, 클라이언트에게 전달되기 전에 응답을 수정하거나, 로깅 등을 할 수 있다. 

필터의 동작 과정

  1. 클라이언트의 요청 수신: 클라이언트로부터 HTTP 요청이 들어오면 웹 서버는 이를 필터 체인(Filter Chain)에 전달한다.
  2. 필터 체인 통과: 요청은 필터 체인을 따라 이동하며, 각 필터는 요청을 처리할 기회를 가진다.
    1. 각 필터는 `doFilter` 메서드를 통해 요청을 처리한다.
    2. 필터는 요청을 다음 필터 또는 서블릿으로 전달할지 여부를 결정할 수 있다.
  3. 서블릿 또는 JSP로 전달: 필터 체인을 모두 통과한 요청은 최종적으로 서블릿이나 JSP에 도달하여 본래의 비즈니스 로직을 수행한다.
  4. 응답 생성: 서블릿이나 JSP가 응답을 생성하면, 응답은 다시 필터 체인을 따라 클라이언트로 돌아간다.
  5. 필터 체인 역순 통과: 응답은 필터 체인을 역순으로 통과하며 각 필터는 응답을 처리할 기회를 가진다.
  6. 클라이언트 응답 전달: 최종적으로 처리된 응답이 클라이언트에게 전달된다.

다음과 같은 필터를 만들어보자!

CustomServletFilter

public class CustomServletFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 작업 (필요한 경우)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 요청 전처리 작업
        System.out.println("Request received at MyFilter");

        // 필터 체인의 다음 요소로 요청을 전달
        chain.doFilter(request, response);

        // 응답 후처리 작업
        System.out.println("Response leaving MyFilter");
    }

    @Override
    public void destroy() {
        // 필터 종료 작업 (필요한 경우)
    }
}

이 필터는 다음과 같은 동작을 한다.

  • 클라이언트로부터 요청이 들어오면 `Request received at MyFilter`를 출력한다.
  • 요청을 다음 필터 또는 서블릿으로 넘긴다.
  • 요청에 대한 응답을 생성한 서블릿 또는 JSP가 응답을 다시 필터로 넘기고 그 응답이 여러 필터를 거쳐 이 필터로 도착한다.
  • `Response leaving MyFilter`를 출력한다. 
  • 응답을 클라이언트에게 최종적으로 전달한다.

 

이렇게 만든 필터를 결국 등록을 해야 사용할 수 있는데, 이 JIRA DC 플러그인을 개발할땐 언제나 리소스는? Add-on Descriptor(atlassian-plugin.xml)에 등록한다. 참고로 JIRA DC 플러그인을 개발하는게 아니면 개발 방식에 따라 필터 등록하는 방법은 다 가지각색이라 목적에 맞는 방법을 찾으면 된다.

 

atlassian-plugin.xml

<servlet-filter key="licenseServletFilter" class="kr.osci.kapproval.admin.servlet.filter.LicenseServletFilter" location="before-dispatch" >
	<url-pattern>/plugins/servlet/kapproval/admin/*</url-pattern>
</servlet-filter>

 

서블릿 필터를 등록하고, url-pattern 태그로 어떤 URL의 요청이 이 필터를 거칠지를 결정하면 된다. 이렇게 설정하면 끝이다.

여기서 location 이라는 attribute가 있다. 이건 이 필터가 어디쯤에 위치할지를 정하는 것이다.

 

나는 `before-dispatch` 라는 값을 주었다. 이게 기본값이고 이건 서블릿 필터 체인의 가장 마지막에 이 필터를 추가하는 것이다. 그러니까 이 요청을 처리하는 서블릿이나 JSP에 도달하기 바로 직전에. 그리고 이러한 옵션들에 대한 내용, 또한 서블릿 필터에 대한 자세한 내용은 아래 공식 문서를 참고하자.

 

Servlet filter

Servlet filter Available:Servlet Filter plugin modules are available in JIRA 4.0 and later. Purpose of this Module Type Servlet Filter plugin modules allow you to deploy Java Servlet filters as a part of your plugin, specifying the location and ordering of

developer.atlassian.com

서블릿 필터가 어떤 원리로 동작하고 어떻게 사용되는지 알아보았다!

 

만약, 요청을 가로채서 하는 작업이 인증/인가를 확인하는 처리라면 인증이 되지 않은 경우 다음 필터 또는 서블릿으로 넘기기 전에 그냥 바로 사용자에게 응답을 돌려줄 수 있다. 예를 들면 이런 코드를 작성할 수 있다.

public class CustomServletFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 작업 (필요한 경우)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       	HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 인증 여부 확인 (여기서는 간단하게 세션에 "authenticated" 속성이 있는지 확인)
        Boolean isAuthenticated = (Boolean) httpRequest.getSession().getAttribute("authenticated");

        if (isAuthenticated == null || !isAuthenticated) {
            // 인증이 안된 경우, 바로 응답 생성
            httpResponse.setContentType("text/html");
            PrintWriter out = httpResponse.getWriter();
            out.println("<html><body>");
            out.println("<h3>Authentication Required</h3>");
            out.println("<p>You are not authenticated. Please <a href=\"login.html\">login</a>.</p>");
            out.println("</body></html>");
            out.close();
        } else {
            // 인증이 된 경우, 다음 필터 또는 서블릿으로 요청 전달
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        // 필터 종료 작업 (필요한 경우)
    }
}

이거는 단순 예시일 뿐, 구현은 원하는대로 어떻게든 할 수 있다!

 

결론

서블릿 필터를 통해 클라이언트의 요청을 가로채서 추가 작업을 할 수 있고 응답을 내보내기 전 마지막 작업을 할 수 있다. 그러려면 Filter를 구현한 서블릿 필터 클래스가 필요하고, 이 클래스는 init, doFilter, destroy 라는 메서드를 재정의해야 하는데 여기서 가장 중요한 건 doFilter 메서드이다. 이 doFilter 메서드에 chain.doFilter()를 호출하기 전에 작성한 코드가 클라이언트의 요청이 서블릿으로 넘어가기 전 작업하는 부분이고 chain.doFilter()를 호출한 이후 코드가 생성된 응답을 클라이언트에게 최종적으로 전달하기 전 작업하는 부분이 된다.

 

 

728x90
반응형
LIST

+ Recent posts