http -> https 이동 시에도 끊기는 증상이 있었는데, 이런 경우는 같은 https 끼리는 세션이 유지되니까 프로토콜을 변경해주는 조치를 간단하게 해줬었다.
예를 들어 url.replace("http://","https://"); 이런식으로..
반대로 https -> http 로 넘어갈 때는 세션이 끊기지 않는다고 알고있었는데, 매번은 아니지만 간혈적으로 끊기는 증상을 발견했다. 그래서 최대한 세션이 끊기지 않도록 Https Filter를 만들어서 세션을 복사하는 필터를 걸어주도록 했다.
아래 소스는 구글에 세션공유 라고만 검색해보면 충분히 얻을 수 있는 소스들이다.
1. web.xml에 필터 등록
web.xml
<filter>
<filter-name>https</filter-name>
<filter-class>내가 만들 Https Filter 패키지 경로.필터클래스명</filter-class>
</filter>
<filter-mapping>
<filter-name>https</filter-name>
<url-pattern>/*</url-pattern> <!-- 모든 url 에 적용 -->
</filter-mapping>
2. HttpsFilter 클래스 & HttpsRequestWrapper 클래스 추가
필터 역할을 할 HttpsFilter와, 그 필터에서 사용할 HttpsRequestWrapper 를 만들어 준다.
HttpsRequestWrapper.java
이 HttpsRequestWrapper 클래스는 HttpServletRequestWrapper 라는, 서블릿으로 들어오는 request를 개발자가 조정할 수 있게하는 래퍼클래스를 상속하여 구현한다.
위 이미지를 기준으로 가장 하단에 위치한, HttpsRequestWrapper 클래스를 구현한다.
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class HttpsRequestWrapper extends HttpServletRequestWrapper{
private HttpServletResponse reponse = null;
public HttpsRequestWrapper(HttpServletRequest request){
super(request);
}
public void setResponse(HttpServletResponse response){
this.response = response;
}
@Override
public HttpSession getSession(){
HttpSession session = super.getSession();
processSessionCookie(session);
return session;
}
@Override
public HttpSession getSession(boolean create){
HttpSession session = super.getSession(create);
processSessionCookie(session);
return session;
}
private void processSessionCookie(HttpSession session){
if(session == null || response == null){
return;
}
// 쿠키가 복사됐는지 flag값 확인
Object cookieOverWritten = getAttribute("COOKIE_OVERWRITTEN_FLAG");
// 쿠키가 복사되지 않았고 && https 요청이고 && 세션 객체가 새로 생성되었다면
if(cookieOverWritten == null && isSecure() && session.isNew()){
Cookie cookie = new Cookie("JSESSIONID",session.getId()); //JSESSIONID 쿠키 생성
cookie.setMaxAge(-1);
String contextPath = getContextPath();
if(contextPath != null && contextPath.length() > 0){
cookie.setPath(contextPath);
}else{
cookie.setPath("/");
}
// 쿠키 세팅 (세션 복제한 쿠키 세팅)
response.addCookie(cookie);
setAttribute("COOKIE_OVERWRITTEN_FLAG","true");
}
}
}
HttpsFilter.java
필터에서 , 위에서 만든 httpsRequestWrapper 클래스를 이용해 reponse 객체를 복제한다. (세션이 유지되도록)
import java.io.IOExcetpion;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HttpsFilter implements Filter{
public HttpsFilter(){}
@Override
public void destroy(){}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{
HttpsRequestWrapper httpsRequest = new HttpsRequestWrapper((HttpServletRequest)request);
httpsRequest.setResponse((HttpServletResponse)response);
chain.doFilter(httpRequest,response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException{}
}
< 필터의 구동순서 설명 >
1. 서블릿 컨테이너는 웹 어플리케이션을 시작할 때 DD파일(web.xml)에 등록된 필터의 인스턴스를 생성하고 init()을 호출한다.
FIlterConfig 객체를 기반으로 FilterChain 객체를 생성한다.
2. web.xml에 등록되어 있는 대로 필터 인스턴스가 생성되고, 첫번째 필터 인스턴스의 doFilter()를 호출한다.
즉, 클라이언트 요청이 들어오면 요청의 패턴에 해당하는 필터의 doFilter()를 호출한다.
3. doFilter()에서 작업을 실행하고 다음 필터의 doFilter()를 호출한다. (= 필터링 결과를 다음 필터에 전달)
4. 마지막 필터까지 3을 반복한다.
5. 마지막 필터는 서블릿의 service()를 호출한다.
따라서 서블릿이 실행되기 전에 처리할 작업은 filterChain.doFilter() 이전에, 서블릿이 실행된 후에 처리할 작업은 filterChain.doFilter() 이후에 작성한다.
6. 서블릿의 service()가 끝나면 service()를 호출했던 이전 필터로 돌아간다.
7. 반복해서 제일 처음 호출됐던 필터까지 돌아간다.
8. 마지막으로 클라이언트에게 응답 결과를 보낸다.
response 파라미터를 사용하여 클라이언트로 가는 응답을 필터링한다.
참고자료
소스에 대한 설명 참고
https://popo015.tistory.com/108
https://jun-itworld.tistory.com/28
필터의 구동 순서
https://junjunrecord.tistory.com/94
Filter vs Interceptor vs AOP 비교 1
https://sallykim5087.tistory.com/158
Filter vs Interceptor vs AOP 비교 2
https://imnotabear.tistory.com/576
'웹개발지식쌓기' 카테고리의 다른 글
[back] Mybatis 쿼리 작동 오류 Query Result[]: -1row (0) | 2023.10.17 |
---|---|
[back] 엑셀 파일 생성 및 다운로드 소스 구조 (0) | 2023.09.01 |
[front] 스크롤 바닥까지 내렸을 때 다음 페이지 가져오기 (부제:웨일 브라우저 이슈) (0) | 2023.06.01 |
[back] 세션 설정 파일의 위치들 (프로젝트, 서버, 리눅스 profile 의 세션 설정) (0) | 2023.06.01 |
[back] association 과 collection 사용 역할 구분 (0) | 2023.06.01 |