학습 목표
· 필터란?
· 필터의 구현
· 필터 설정하기: web.xml 이용
· 필터 설정하기: @WebFilter 애노테이션 이용
· 필터의 응용
- 로그인 검사 필터
- TODO: XSL/T 필터
- TODO: 캐릭터 인코딩 필터
필터란?
· HTTP 요청과 응답을 변경할 수 있는 재사용 가능한 클래스
· 특징:
- 객체 형태로 존재
- 클라이언트에서 오는 요청(request)과 최종 자원 사이에 위치하여, 클라이언트의 요청 정보를 알맞게 변경 가능
- 최종 자원과 클라이언트로가는 응답(response) 사이에 위치하여, 최종 자원의 요청 결과를 알맞게 변경 가능
· 클라이언트와 자원 사이에 여러 개의 필터가 모여 하나의 필터 체인을 형성할 수 있음
· 필터는 정보 뿐만 아니라 흐름도 변경할 수 있음
- 즉, 클라이언트의 요청을 필터 체인의 다음 단계(클라이언트가 요청한 자원)에 보내는 것이 아니라
다른 자원의 결과를 클라이언트에 전송할 수 있음
- 이러한 기능은 사용자 인증이나 권한 검사 등에 용이하게 사용할 수 있음
필터의 구현
· 필터 구현을 위한 핵십 타입 세 가지 - 개발자는 세 타입을 이용해서 필터를 구현하고, 요청과 응답 정보를 변경하는 기능을 구현
1. javax.servlet.Filter 인터페이스 : 클라이언트와 최종 자원 사이에 위치하는 필터를 나타내는 객체가 구현해야 하는 인터페이스
2. javax.servlet.ServletRequestWrapper 클래스 : 필터가 요청을 변경한 결과를 저장하는 래퍼
3. javax.servlet.ServletResponseWrapper 클래스: 필터가 응답을 변경하기 사용하는 래퍼
· 필터 클래스를 컴파일하려면 서블릿 API가 포함된 jar 파일을 클래스패스에 추가해야함
Filter 인터페이스
· 필터 기능을 제공할 클래스는 Filter 인터페이스를 알맞게 구현해주어야함
· Filter 인터페이스에 선언된 메서드:
public void init(FilterConfig filterConfig) throws ServletException
// 필터를 초기화할 때 호출
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException
// 필터 기능을 수행, chain을 이용해서 체인의 다음 필터로 쿼리 전달
public void destory()
// 필터가 웹 컨테이너에서 삭제될 때 호출
· 서블릿 컨테이너는 사용자가 특정한 자원을 요청했을 때, 그 자원 사이에 필터가 존재하는 경우 필터 객체의 doFilter() 메서드를 호출함
- 이 시점부터 필터를 적용하기 시작
▶ 예시 - 필터의 구현 방법
public class FirstFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 초기화 작업
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException) throws IOException, ServletException {
// 1. request 파라미터를 이용하여 요청의 필터 작업 수행
RequestWrapper 클래스를 사용해서 클라이언트 요청을 변경 가능
...
// 2. 체인의 다음 필터 처리
요청의 필터링 결과를 다음 필터에 전달
chain.doFilter(request, response);
// 3. response를 이용하여 응답의 필터링 작업 수행
체인을 통해서 전달된 응답 데이터를 변경하여 그 결과를 클라이언트에 전송
...
}
public void destroy(){
// 주로 필터가 사용한 자원을 반납
}
}
· doFilter() 메서드는 요청이 있을 때마다 매번 실행
· doFilter 메서드의 세 번째 파라미터 FilterChain 객체는 클라이언트가 요청한 자원에 이르기까지 클라이언트의 요청이 거쳐 가게 되는 필터 체인을 의미
- FilterChain을 사용해서 필터는 체인에 있는 다음 필터에 변경한 요청과 응답을 전달 가능
· 요청을 필터링 한 필터 객체가 또다시 응답을 필터링함
- 1단계와 3단계 사이에 다음 필터로 이동하기 때문에 요청의 필터 적용순서와 응답의 필터 적용 순서는 반대
· init() 메서드에 전달되는 FilterConfig: 필터의 초기화 파라미터를 읽어올 때 사용
· FilterConfig가 제공하는 메서드
메서드 | 리턴 타입 | 설명 |
getFilterName() | String | · 설정 파일에서 <filter-name>에 지정한 필터의 이름을 리턴 |
getInitParameter(String name) | String | · 설정 파일의 <init-param>에서 지정한 초기화 파라미터 값을 읽어옴 · 존재하지 않는 경우 null 리턴 |
getInitParameterNames() | Enumeration<String> | · 초기화 파라미터의 이름 목록을 구함 |
getServletContext() | ServletContext | · 서블릿 컨텍스트 객체를 구함 |
필터 설정하기: web.xml 이용
· web.xml 파일에 필터를 설정하려면, <filter> 태그와 <filter-mapping> 태그를 사용
<?xml version="1.0" encoding="utf-8"?>
<web-app ...>
<filter>
<filter-name>FilterName</filter-name>
<filter-class>javacan.filter.FileClass</filter-class>
<init-param>
<param-name>paramName</param-name>
<param-value>value</param-value>
</init-pram>
</filter>
<filter-mapping>
<filter-name>FilterName</filter-name>
<url-pattern>*.jsp</url.patter>
</filter-mapping>
...
</web-app>
· <filter> 태그: 웹 어플리케이션에서 사용할 필터 지정
· <filter-mapping> 태그: 특정 자원에 대해 어떤 필터를 사용할지 지정
- 위 예제는 클라이언트가 jsp 확장자를 갖는 경로를 요청한 경우 FilterName 필터를 적용하도록 설정
· <init-param> 태그: 필터를 초기화할 때, 즉 필터의 init() 메서드를 호출할 때 전달할 파라미터를 설정
- 주로 필터를 사용하기 전에 초기화 작업에 필요한 정보를 제공하기 위해 사용
· <url-pattern> 태그: 클라이언트가 요청한 특정 URI에 대해서 필터링 할 때 사용
- 서블릿에서 <url-pattern>과 동일한 규칙을 갖음 https://scshim.tistory.com/397
· URL 패턴이 서블릿을 매핑하는 규칙:
- '/'로 시작하고 '/*'로 끝나는 url-pattern은 경로 매핑을 위해서 사용
- '*.'로 시작하는 url-pattern은 확장자에 대한 매핑을 할 때 사용
- 오직 '/'만 포함하는 경우 어플리케이션의 기본 서블릿으로 매핑
- 이 규칙 외, 나머지 다른 문자열은 정확한 매핑을 위해 사용
· <url-pattern> 태그 대신 <servlet-name> 태그를 사용하면, 특정 서블릿에 대한 요청에 대해서 필터를 적용함
▶ 예시 - <servlet-name> 태그를 사용해서 FileDownload인 서블릿에 대해 AuthCheckFilter를 필터로 사용
<filter-mapping>
<filter-name>AuthCheckFilter</filter-name>
<servlet-name>FileDownLoad</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>FileDownLoad</servlet-name>
...
</servlet>
· <dispatcher> 태그를 사용하면 필터가 적용되는 시점을 설정 가능
- <dispatcher> 태그는 <filter-mapping> 태그의 자식 태그로 사용됨
<filter-mapping>
<filter-name>AuthCheckFilter</filter-name>
<servlet-name>FileDownLoad</servlet-name>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
· <dispatcher> 태그가 가질 수 있는 값:
- REQUEST: 클라이언트의 요청인 경우 필터를 적용 (기본값)
- FORWARD: forward()를 통해서 제어 흐름을 이동하는 경우에 필터 적용
- INCLUDE: include()를 통해서 포함되는 경우에 필터 적용
▶ 예시 - <dispatcher> 태그 사용하기
<filter-mapping>
<filter-name>AuthCheckFilter</filter-name>
<url-pattern>/pds/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>IPCheckFilter</filter-name>
<url-pattern>/pds/data/*</url-pattern>
<dispatcher>INCLUDE</dispathcer>
</filter-mapping>
· 웹 브라우저 /pds/data/download.jsp를 요청하면,
두 개의 필터 매핑 모두 <url-pattern>에 따라 이 URL을 처리할 수 있지만,
두 번째 필터 매핑은 <dispatcher>의 값이 INCLUDE이므로 웹 브라우저의 요청에 대해서는 필터 적용 x
· 아래와 같이 <jsp:include> 코드를 사용했을 경우,
/pds/data/util.jsp는 두 개의 필터 모두에 해당되지만,
첫 번째 필터는 웹 브라우저의 요청에 대해서만 적용되기 때문에 사용되지 않고,
<dispatcher>의 값이 INCLUDE인 두 번째 필터만 사용
<jsp:include page="/pds/data/util.jsp" flush="false" />
· 웹 브라우저의 요청이 동시에 여러 개의 필터 매핑에 적용되는 경우,
web.xml 파일에 표시한 순서대로 필터를 적용
<filter-mapping>
<filter-name>AuthCheckFilter</filter-name>
<url-pattern>/pds/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>IPCheckFilter</filter-name>
<url-pattern>/pds/data/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CompressFilter</filter-name>
<url-patter>/pds/data/*</url-patter>
</filter-mapping>
· 위와 같은 경우 웹 브라우저가 /pds/data/a.jsp를 요청하면 필터는
AuthCheckFilter->IPCheckFilter->CompressFilter 순서대로 실행
· 하나의 <filter-mapping>에서 한 개 이상의 <url-pattern> 태그와 <servle-name> 태그를 설정할 수 있음
<filter-mapping>
<filter-name>IPCheckFilter</filter-name>
<url-pattern>/pds/data/*</url-pattern>
<url-pattern>/webinterface/*</url-pattern>
<servlet-name>DownloadServlet</servlet-name>
</filter-mapping>
필터 설정하기: @WebFilter 애노테이션 이용
· Filter 클래스가 @WebFilter 애노테이션을 가지면 자동으로 필터로 등록됨
▶ 예시 - @WebFilter 애노테이션을 사용하는 방법
import javax.servlet.annotation.WebFilter;
@WebFilter(filterName = "xsltFilter", urlPatterns = "{/xml/*"})
public class XSLTFilter implements Filter{
... 필터 구현
}
· 위와 같은 경우 /xml/*에 일치하는 URL에 필터가 적용됨
· 두 개 이상의 URL 패턴을 지정하고 싶다면, 콤마로 구분해서 여러 URL 패턴을 지정
@WebFilter(filterNamer = "xsltFiler", urlPatterns = {"/xml/*", "/xsl/*"})
public class XSLTFilter implements Filter{
· @WebFilter 애노테이션의 주요 속성
- urlPatterns: 필터를 적용할 URL 패턴 목록을 지정
- servletName: 필터를 적용할 서블릿 이름 목록 지정
- filterName: 필터의 이름을 지정
- initParams: 초기화 파라미터 목록을 지정
- dispatcherTypes: 필터를 적용할 범위를 지정, 열거 타입 DispatcherType에 정의된 값을 사용,
기본값은 DispatchType.REQUEST
요청 및 응답 래퍼 클래스
· 클라이언트의 요청과 응답을 변경할 때 ServletRequestWrapper, ServletResponseWrapper를 사용함
- 위의 래퍼 클래스들로 할 수 있는 일:
1. 요청 정보를 변경하여 최종 자원인 서블릿/JSP/HTML/기타 자원에 전달
2. 최종 자원으로부터 응답을 변경하여 샐운 응답 정보를 클라이언트에 보냄
· 서블릿의 요청 래퍼와 응답 래퍼를 만들려면 javax.servlet 패키지에 정의된
ServletRequestWrapper, ServletResponseWrapper 클래스를 상속 받아 구현함
· 대부분 필터는 HTTP 프로토콜에 대한 요청·응답을 필터링 하므로 위의 두 클래스를 상속받아 알맞게 구현한 HttpServletRequestWrapper, HttpServletResponseWrapper 클래스를 상속 받아 구현하는 것을 권장함
- java.servlet.http 패키지에 정의
- HttpServletRequest, HttpServletResponse 인터페이스에 정의된 모든 메서드가 구현되어있음
· 필터를 통해 변경하고 싶은 요청 정보가 있으면 ,
HttpServletRequestWrapper 클래스를 상속받은 클래스를 만들고,
그 정보를 추출하는 메서드를 알맞게 재정의해서 변경한 정보를 제공하도록 구현하고,
구현한 래퍼 클래스의 객체를 FilterChain의 doFilter() 메서드에 넘져준다.
필터의 응용
· 필터를 적용하는 대표적인 기능:
1. 사용자 인증
2. 캐싱 필터
3. 자원 접근에 대한 로깅
4. 응답 데이터 변환(HTML 변환, 응답 헤더 변환, 데이터 암호화 등)
5. 공통 기능 실행
로그인 검사 필터
· 사용자 인증을 구현하기 위해 인증 여부를 검사하는 필터를 사용할 수 있음
- 웹브라우저의 요청은 서블릿/JSP에 전달되기 전에 필터를 통과하므로,
필터에서 조건에 따라 알맞게 흐름 제어를 할 수 있음
- 즉, 필터에서 로그인 여부를 판단 -> 로그인하지 않았다면 로그인 폼을 보여주는 페이지로 흐름을 이동시킬 수 있음
- JSP/서블릿 등의 코드에 사용자 로그인 판단 여부를 확인하는 코드를 넣으면,
회원 인증 정책이 변경될 때마다 관련된 모든 코드를 변경해야하는 문제가 발생
▶ 예시 - session에 "MEMBER" 속성이 존재하면 로그인한 것으로 판단하는 필터인 LoginCheckFilter 클래스
public class LoginCheckFilter implements Filter{
@Override
public void init(FilterConfig config) throws ServletException{}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{
HttpServletRequest httpRequeset = (HttpServletRequest)request;
HttpSession session = httpRequeset.getSession(false);
boolean login =false;
if(session != null)
if(session.getAttribute("MEMBER") != null)
login = true;
//로그인한 상태라면
if(login){
//필터 체인의 다른 필터로 이동하고
chain.doFilter(request, response);
} else {
//로그인하지 않은 상태라면 로그인 페이지로 이동
RequestDispatcher dispatcher = request.getRequestDispatcher("/loginForm.jsp");
dispatcher.forward(request, response);
}
}
@Override
public void destroy(){}
}
· LoginCheckFilter를 테스트하기 위해 web.xml 파일에 다음과 같은 필터 설정을 추가
<filter>
<filter-name>LoginCheck</filter-name>
<filter-class>filter.LoginCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginCheck</filter-name>
<url-pattern>/board/*</url-pattern>
</filter-mapping>
loginForm.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>로그인</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/login.jsp">
아이디<input type="text" name="memberId">
비밀번호<input type="password" name="password" %>
<input type="submit" value="로그인">
</form>
</body>
</html>
login.jsp
<%@ page contentType="text/html; charset=utf-8" %>
<%
String memberId = request.getParameter("memberId");
session.setAttribute("MEMBER", memberId);
%>
<html>
<body>
로그인 처리
</body>
</html>
TODO: XSL/T 필터
TODO: 캐릭터 인코딩 필터
출처
최범균의 JSP 2.3 웹 프로그래밍: 기초부터 중급까지
'스프링 > JPA' 카테고리의 다른 글
[JPA] Spirng Data JPA에서 QueryDSL 사용하기 (0) | 2021.07.20 |
---|---|
[JPA] 오류 해결: “Data too long for column” (0) | 2021.05.29 |
[JPA] 오류해결: Field 'id' doesn't have a default value (0) | 2021.05.27 |
[JPA] JPA란? (0) | 2021.04.20 |
댓글