이번에는 서블릿 필터를 만들어보자. 서블릿 필터는 사실 그냥 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가 응답을 만들고, 클라이언트에게 전달되기 전에 응답을 수정하거나, 로깅 등을 할 수 있다.
필터의 동작 과정
- 클라이언트의 요청 수신: 클라이언트로부터 HTTP 요청이 들어오면 웹 서버는 이를 필터 체인(Filter Chain)에 전달한다.
- 필터 체인 통과: 요청은 필터 체인을 따라 이동하며, 각 필터는 요청을 처리할 기회를 가진다.
- 각 필터는 `doFilter` 메서드를 통해 요청을 처리한다.
- 필터는 요청을 다음 필터 또는 서블릿으로 전달할지 여부를 결정할 수 있다.
- 서블릿 또는 JSP로 전달: 필터 체인을 모두 통과한 요청은 최종적으로 서블릿이나 JSP에 도달하여 본래의 비즈니스 로직을 수행한다.
- 응답 생성: 서블릿이나 JSP가 응답을 생성하면, 응답은 다시 필터 체인을 따라 클라이언트로 돌아간다.
- 필터 체인 역순 통과: 응답은 필터 체인을 역순으로 통과하며 각 필터는 응답을 처리할 기회를 가진다.
- 클라이언트 응답 전달: 최종적으로 처리된 응답이 클라이언트에게 전달된다.
다음과 같은 필터를 만들어보자!
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()를 호출한 이후 코드가 생성된 응답을 클라이언트에게 최종적으로 전달하기 전 작업하는 부분이 된다.
'Jira & ScriptRunner' 카테고리의 다른 글
Jira DC 플러그인 개발 Part.10 - 로그 설정 (0) | 2024.08.13 |
---|---|
Jira DC 플러그인 개발 Part.8 이벤트 리스너 등록하기 (0) | 2024.07.23 |
Jira DC 플러그인 개발 Part.6 - JQL Function 만들기 (0) | 2024.07.16 |
Jira DC 플러그인 개발 Part.5 - 상단 네비게이션 바에 앱 링크 노출하기 (0) | 2024.07.16 |
Jira DC 플러그인 개발 Part.4 - ActiveObjects ORM (0) | 2024.07.15 |