📌 문제상황
현재 SW마에스트로에서 개발한 앱에서 로그인 관련한 이슈가 다수 발생했다.
앱 실행했을 때 로그인 버튼이 안 나오거나, 세션이 만료되지 않았음에도 불구하고 앱을 껐다 키면 다시 로그인을 해야하는 문제가 있었다.
해당 문제를 해결하려고 하는 도중에 FE 개발자가 "쿠키에 아무것도 들어가지 않았는데 API가 호출이 된다."라는 말을 했다.
아마 클라이언트 단에서 쿠키를 캐싱하는 기능이 있나 했다... (이 부분에 대한 의문은 아직 풀지 못한 상태이다.)
일단 해당 현상을 재현하기 위해 Postman을 통해 Cookie에 아무것도 존재하지 않을 때 API를 호출했다.
그런데 이게 무슨 일인지, 세션을 발급해야 하는 API가 아님에도 불구하고 Cookie에 세션이 담겨 있는 것을 확인할 수 있었다.
로그인 관련한 이슈가 해당 부분에서 발생한 것일수도 있기도 하며, 애초에 이렇게 동작하면 안 되는 코드이기도 해서 해당 부분을 해결하고자 했다.
📌 해결방법
모든 API에 대해 해당 문제가 발생한 것으로 보아, Session이 존재하는지 확인하는 과정에서 해당 문제가 발생했다고 생각했다.
SessionMemberInterceptor.java, LoginMemberArgumentResolver.java, WebConfig.java와 같이 API 호출 전에 Session이 있는지 확인하는 코드를 위주로 살펴봤다.
▶️ HttpSession 객체의 사용 방식에 따른 차이
먼저 아래와 같이 코드를 작성했을 때, 세션이 없으면 새롭게 세션이 생성 됐다.
@Slf4j
@RequiredArgsConstructor
@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
private final HttpSession httpSession;
@Override
public boolean supportsParameter(MethodParameter parameter) {
log.info("LoginMemberArgumentResolver.supportsParameter() 실행");
boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginMember.class) != null; // @LoginMember 어노테이션이 붙어있는지 확인
boolean isUserClass = SessionMember.class.equals(parameter.getParameterType()); // 파라미터 클래스 타입이 SessionMember.class 인지 확인
return isLoginUserAnnotation && isUserClass; // true인 경우 세션 재발급.
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception{
log.info("LoginMemberArgumentResolver.resolveArgument() 실행");
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false); // 세션이 없으면 null 반환
SessionMember member = (SessionMember) httpSession.getAttribute("member");
if (member == null) {
throw new SessionMemberNotFoundException();
}
return member;
}
}
하지만, 하단의 코드의 경우 세션이 새롭게 생성되거나 하지 않았다.
@Slf4j
@RequiredArgsConstructor
@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
log.info("LoginMemberArgumentResolver.supportsParameter() 실행");
boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginMember.class) != null; // @LoginMember 어노테이션이 붙어있는지 확인
boolean isUserClass = SessionMember.class.equals(parameter.getParameterType()); // 파라미터 클래스 타입이 SessionMember.class 인지 확인
return isLoginUserAnnotation && isUserClass; // true인 경우 세션 재발급.
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception{
log.info("LoginMemberArgumentResolver.resolveArgument() 실행");
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false); // 세션이 없으면 null 반환
if (session == null) {
throw new SessionMemberNotFoundException();
}
SessionMember member = (SessionMember) session.getAttribute("member");
if (member == null) {
throw new SessionMemberNotFoundException();
}
return member;
}
}
두 코드의 차이는 HttpSession 객체를 어떻게 처리하냐의 차이에 있는데, 첫 번째 코드의 경우 HttpSession을 필드로 직접 주입받으면서 해당 요청에 세션이 존재하지 않으면 내부적으로 새롭게 세션을 생성할 수 있다고 한다.
두 번째 코드에서는 현재 요청의 세션을 직접 조회하며, getSession을 통해 세션을 불러올 때 argument로 false를 이용하여 세션이 없더라도 새롭게 생성하지 않는다.
📌 결과
이전에 쿠키가 생성됐던 코드를 다시 실행했을 때, 쿠키가 생성되지 않는다!
'Java > Spring' 카테고리의 다른 글
[Spring] request.getSession()과 request.getSession(false)의 차이를 알아보자. (0) | 2023.11.14 |
---|---|
[Spring] spring_session 테이블의 expiry_time이 변경되지 않는 문제 (0) | 2023.10.16 |
[Spring] server.servlet.session.timeout과 spring.session.timeout의 차이 (0) | 2023.10.13 |
[Spring] OAuth2 소셜 로그인 후 Postman에서 Session 설정하기 (0) | 2023.09.30 |
[Spring] Spring Security에서 발생한 403 Forbidden 에러 처리 (0) | 2023.09.26 |