JSESSIONID
라는 이름으로 쿠키를 생성개념 : 사용자의 상태를 유지하기 위해 클라이언트 측에 데이터를 저장하는 방식입니다. 클라이언트가 서버에 요청을 보낼 때마다 해당 쿠키를 함께 전송하여 서버는 클라이언트를 식별하고 상태 정보를 유지할 수 있게 됩니다.
클라이언트에 저장되는 key, value가 들어간 데이터 파일로 서버에서 HTTP Response Header에 Set-Cookie
속성을 이용해 클라이언트에 쿠키 제공
이름-값(쿠키이름은 숫자와 알파벳으로만 구성된다), 만료 날짜/시간 (쿠키 저장기간), 경로 정보 포함, 도메인, 보안, HttpOnly 속성
서버에서 로그인에 성공하면 HTTP응답을 담은 쿠키를 웹에 전송하고 웹은 쿠키 저장소에 쿠키를 저장
웹은 모든 요청을 할 때, HTTP 헤더에 쿠키를 같이 보냄. 따라서 추가 요청을 할 때 쿠키의 값을 참고하여 인증 없이 접근이 가능
쿠키에는 영속 쿠키와 세션 쿠키가 있다.
(1)영속 쿠키
: 만료 날짜를 입력하면 해당 날짜까지 유지
(2)세션 쿠키
: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지
(String타입)
은 회원의 id 를 담아둔다. 웹 브라우저는 종료 전까지 회원의 id 를 서버에 계속 보내줄 것이다@PostMapping("/login")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult
bindingResult, HttpServletResponse response)
//로그인 성공처리 후
//쿠키에 시간 정보를 주지 않으면 세션 쿠키(브라우저 종료시 모두 종료)
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
Cookie[] cookies = request.getCookies();
@GetMapping("/")
public String homeLogin(
@CookieValue(name = "memberId", required = false) Long memberId,
Model model) {
if (memberId == null) {
return "home";
}
//로그인
Member loginMember = memberRepository.findById(memberId);
if (loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
}
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
expireCookie(response, "memberId");
return "redirect:/";
}
private void expireCookie(HttpServletResponse response, String cookieName) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
컨트롤러 메소드(@CookieValue(value="쿠키이름", required=false, defaultValue="기본값") String 변수명)
중요한 정보를 모두 서버에 저장해야 한다. 그리고 클라이언트와 서버는 추정 불가능한 임의의 식별자 값으로 연결해야 한다.
일정시간동안 세션이 계속 유지되기 때문에, 동시접속자 수가 많은 사이트의 경우 서버에 과부하를 주게 되므로 성능이 저하될 수 있다.
비동기 통신을 하면 세션이 필요없어진다
세션아이디
를 key값으로 생성하고 value는 멤버
로 세션저장소에 저장 즉, 사용자의 정보를 서버에 저장(stateless 위반)
@PostMapping("/login")
public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult
bindingResult, HttpServletRequest request)
// 쿠키에 들어있는 세션아이디를 조회했을 때, 세션이 있으면 있는 세션 반환,
없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션저장소에 로그인 회원 정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
@PostMapping("/logout")
public String logoutV3(HttpServletRequest request) {
//세션을 삭제한다.
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
@GetMapping("/")
public String homeLoginV3(HttpServletRequest request, Model model) {
//세션이 없으면 home
HttpSession session = request.getSession(false);
if (session == null) {
return "home";
}
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
http://localhost:8080/;jsessionid=F59911518B921DF62D09F0DF8F83F872
server.servlet.session.tracking-modes=cookie
// URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶으면 다음 옵션을 넣어주면 된다.
이렇게 하면 URL에 jsessionid 가 노출되지 않는다.
서버 입장에서 웹 브라우저가 쿠키를 지원하는지 하지 않는지 최초에는 판단하지 못하므로, 쿠키 값도 전달하고, URL에 jsessionid도 함께 전달한다. 사용하지 않는 방법이므로 URL에 노출되지 않게 설정한다.
대부분의 사용자는 로그아웃을 선택하지 않고, 그냥 웹 브라우저를 종료한다. 문제는 HTTP가 비연결성(ConnectionLess)이므로 서버 입장에서는 해당 사용자가 웹 브라우저를 종료한 것인지 아닌지를 인식할 수 없다. 따라서 서버에서 세션 데이터를 언제 삭제해야 하는지 판단하기가 어렵다
따라서 세션의 종료시점은 세션 생성 시점이 아니라 사용자가 서버에 최근에 요청한 시간을 기준으로 30분 정도를 유지해주는 것이다
maxInactiveInterval
: 세션의 유효 시간, 예) 1800초, (30분)
lastAccessedTime
: 세션과 연결된 사용자가 최근에 서버에 접근한 시간, 클라이언트에서 서버로 sessionId ( JSESSIONID )를 요청한 경우에 갱신된다.
server.servlet.session.timeout=60 : 60초, 기본은 1800(30분) ->글로벌설정
session.setMaxInactiveInterval(1800); //1800초 -> 특정 세션설정
LastAccessedTime 이후로 timeout 시간이 지나면, WAS가 내부에서 해당 세션을 제거한다
@SessionAttributes
을 지원한다. @SessionAttributes 파라미터로 지정된 이름과 같은 이름이 @ModelAttribute에 지정되어 있으면, @ModelAttribute가 설정된 메서드가 반환되는 값을 그 세션에 저장한다.@Controller
@SessionAttributes("user")
public class LoginController {
@ModelAttribute("user")
public User setUpUserForm() {
return new User();
}
}
@Controller
@SessionAttributes("user")
public class LoginController {
@PostMapping("/dologin")
public String doLogin(@ModelAttribute("user") User user, Model model) {
...
}
}
@GetMapping("/info")
public String userInfo(@SessionAttribute("user") User user {
...
return "user";
}
SessionStatus
는 컨트롤러 메서드의 파라미터로 사용할 수 있는 스프링 내장 타입이다. SessionStatus의 setComplete 메서드로 user라는 이름의 세션 값을 제거한다.@Controller
@SessionAttributes("user")
public class UserController {
@RequestMapping(value="/user/add", method=RequestMethod.POST)
public String submit(@ModelAttribute("user") User user, SessionStatus sessionStatus) {
...
sessionStatus.setComplete();
...
}
}
세션이 있는지, 세션아이디에 맞는 객체가 있는지 다 자동으로 체크해줌
@SessionAttributes는 세션을 생성하는 기능은 없음
public class LoginArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
boolean hasLoginAnnotation = methodParameter.hasParameterAnnotation(Login.class);
boolean hasLongType = Long.class.isAssignableFrom(methodParameter.getParameterType());
return hasLoginAnnotation && hasLongType;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
HttpSession session = request.getSession(false);
if(session == null){ //세션이 없으면 null 반환
return null;
}
return session.getAttribute("userId");
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new LoginArgumentResolver());
}
}
세션(Session)과 쿠키(Cookie)는 웹 애플리케이션에서 사용자의 상태를 유지하기 위한 메커니즘이다.
세션 : 클라이언트에 고유한 세션 ID를 부여하고, 이를 통해 클라이언트와 관련된 데이터를 서버에 저장한다. 저장용량은 DB나 메모리에 따라 달라진다. 클라이언트가 비활성 상태일 때 자동으로 만료될 수 있다. 만료 시간은 서버에서 설정할 수 있다.
쿠키 : 클라이언트의 웹 브라우저에 저장되며, 클라이언트의 요청 시에 서버로 전송된다. 데이터 형태는 Key, Value 한 쌍으로 구성되고 String 형태이다.
-참고
https://backendcode.tistory.com/176
https://enai.tistory.com/29