웹은 HTTP 프로토콜을 사용한다.
Stateless 한 구조이기 때문에 인증/인가를 위해 유저 정보를 어딘가에 저장해야할 필요가 있다.
그래서 클라이언트와 서버는 쿠키에 암호화된 키를 주고 받으며 인증/인가의 역할을 수행한다.
스프링은 서블릿 컨테이너에서 생성하는 HttpSession을 받아 사용한다.
세션은 서버에서 관리하는 인증/인가의 수단으로 보안을 위해 암호화된 키를 통해 접근할 수 있다.(키를 쿠키에 담아 소통한다.)
세션을 가져오는 방법은 다양하기 때문에 여러 방법들을 알아보자.
[의존성 주입을 통해 받는 방법]
@Component
public class SessionDIExample {
@Autowired
private void HttpSession httpSession;
private static final String USER_ID = "USER_ID";
public void save() {
final String userId = "testUser";
httpSession.setAttribute(USER_ID, userId);
}
public void get() {
String userId = (String) httpsession.getAttribute(USER_ID);
}
}
HttpSession의 scope는 session scope를 가진다.
즉, 브라우저의 최초 요청부터 브라우저 종료까지의 생명 주기를 가진다.
이 때 서블릿 컨테이너에서 생성된 HttpSession은 스프링에 언제 주입될까?
바로 .setAttribute, .getAttribute 같은 메서드가 호출 될 때이다.
해당 메서드들이 호출될 때 동적으로 프록시 객체를 생성해 주입한다.
[파라미터에서 가져오는 방법]
@RestController
public class SessionParamExample {
private static final String USER_ID = "USER_ID";
//@SessionAttribute로 가져오는 법
@GetMapping("/annotaion")
public void getByAnnotation(@SessionAttribute(USER_ID) String userId) {
..
}
//HttpSession을 파라미터로 주입 받는 법
@GetMapping("/HttpSession")
public void getByParameter(HttpSession httpSession) {
String userId = (String) httpSession.getAttribute(USER_ID);
}
//HttpRequest 파라미터로 주입 받는 법
@GetMapping("/HttpRequest")
public void getByParameter(HttpServletRequest request) {
Httpsession httpSession = request.getSession();
String userId = (String) httpSession.getAttribute(USER_ID);
}
}
파라미터로 가져오는 방법은 다양하지만 세션에 의존하게 된다.
예를들어 로그인 체크 기능을 상상해보자.
특정 기능을 수행하기 위해 세션에 저장된 memberId가 있는지만 확인하면 되는데, 어쩌면 컨트롤러의 역할과 무관해 보인다.
즉, 로그인 체크와 컨트롤러를 분리해도 로직상 문제가 없지만 파라미터를 통해 받았기 때문에 컨트롤러 메서드 안에서 체크해야한다.
또한, 로그인 체크가 필요한 기능마다 getAttribute()를 호출해야 하므로 중복코드의 생성도 늘어난다.
[RequestContextHolder로 가져오는 방법]
스프링 프레임워크의 범위 내에서라면 어디서든 HttpSession을 가져올 수 있는 방법이 있다.
스프링은 Servlet이 호출 되었을 때 <K:V> 로 로 request를 저장한다.(thread-safe하다.)
따라서 별도의 어노테이션이나 파라미터로 주입받지 않아도 스프링을 통해 HttpServletRequest를 가져올 수 있다.Request에 포함된 SessionId로 전역에서 관리 중인 Manager를 통해 세션을 얻어온 후 세션을 이용할 수 있다.
public class SessionUtilExample {
public static HttpServletRequest getRequest() {
return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
.getRequest();
}
public static HttpSession getSession() {
return getRequest().getSession();
}
}
RequestContextHolder의 장점은 Util 클래스로 만들 수 있다는 것이다.
DI 방법과 비슷하지만 Util 클래스를 bean 으로 등록하지 않아도 되며, thread-safe 하기 때문에 전역에서 사용할 수 있다.