일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Today
- Total
- autowired
- 스프링
- docker
- 스프링 Configuration
- 스프링 빈
- 생성자 주입
- 도커
- 스프링 싱글톤
- ComponentScan
- beandefinition
- 라즈베리파이
- DI컨테이너
- HandlerMethodArgumentResolver
- 의존관계 주입
- Spring interceptor
- 라즈베리파이4
- RequiredArgsConstructor
- UsernamePasswordAuthenticationFilter
- springsecurity
- 빈 중복 오류
- 스프링 컨테이너
- DI
- 싱글톤 컨테이너
- Spring
- qualifier
- Autowired 옵션
- 롬복 Qualifier
- 스프링 빈 조회
- 객체지향
- Servlet Filter
그날그날 공부기록
쿠키, 세션 사용해보기 본문
231126 쿠키, 세션
회원의 로그인 정보를 유지시켜야 하는 상황.
홈 → 로그인 → 메인 페이지 이동(홈)
우선 쿠키에 사용자 정보를 보관해 보자.
다음은 로그인 컨트롤러의 login 메서드이다.
@PostMapping("/login")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
// 로그인 성공 처리
// 쿠키에 시간 정보를 주지 않으면 세션 쿠키(브라우저 종료시 모두 종료)
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
return "redirect:/";
}
form 데이터를 LoginForm(DTO)로 바인딩하고 문제가 있다면 bindingResult에 에러 정보가 담긴다.
올바른 사용자인지 확인한 후, Cookie 객체에 name과 value를 넣어 HttpServletResponse에 넣어 반환한다.
setMaxAge(int age)
로 쿠키 유지 시간을 설정할 수 있고,
음수 or 설정X는 세션쿠키로 설정되어 브라우저가 종료된다면 해당 쿠키는 삭제된다.
age를 0으로 설정한다면 해당 name의 쿠키는 즉시 삭제된다.
세션쿠키로 브라우저에 저장된 것을 확인할 수 있다.
로그인이 완료되고, 홈 컨트롤러의 homeLogin 메서드가 실행된다.@CookieValue
를 사용하면 HttpServletRequest에서 쿠키를 일일이 찾지 않아도 된다.
@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";
}
하지만 쿠키 값은 브라우저에 저장되어 쉽게 볼 수 있고, 사용자가 임의로 변경할 수 있다.
따라서 쿠키 값에는 중요한 정보를 보관하면 안 된다.
중요한 정보를 서버에 저장하기 위해 세션을 사용해 보자.
쿠키를 사용하긴 하지만 기존에 저장하던 사용자의 정보 대신, 랜덤한 토큰값을 저장한다.
그리고 서버에는 이 토큰값을 Key, 사용자 정보를 Value로 하여 저장해 둔다.
이렇게 한다면 토큰을 예측, 변조하기 힘들고, 탈취당하더라도 해당 세션정보를 삭제하면 된다.
세션을 생성, 조회, 만료 기능만 간단히 구현하였다.
@Component
public class SessionManager {
public static final String SESSION_COOKIE_NAME = "mySessionId";
private Map<String, Object> sessionStore = new ConcurrentHashMap<>();
/*세션 생성*/
public void createSession(Object value, HttpServletResponse response) {
// session id 생성
String sessionId = UUID.randomUUID().toString();
// 세션 저장소에 정보 저장
sessionStore.put(sessionId, value);
// sessionId로 응답 쿠키를 생성하여 클라이언트에 전달
Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId);
response.addCookie(mySessionCookie);
}
/*세션 조회*/
public Object getSession(HttpServletRequest request) {
// 쿠키 확인
Cookie sessionCookie = findCookie(request);
if (sessionCookie == null) {
return null;
}
// 쿠키에 저장되어 있던 key로 세션 저장소의 정보 가져오기
return sessionStore.get(sessionCookie.getValue());
}
/*세션 만료*/
public void expire(HttpServletRequest request) {
Cookie sessionCookie = findCookie(request);
if (sessionCookie != null) {
sessionStore.remove(sessionCookie.getValue());
}
}
public Cookie findCookie(HttpServletRequest request) {
if (request.getCookies() == null) {
return null;
}
// 쿠키들에서 세션에 관련된 쿠키 가져오기
return Arrays.stream(request.getCookies()).filter(cookie -> cookie.getName()
.equals(SESSION_COOKIE_NAME))
.findFirst()
.orElse(null);
}
}
이제 세션을 바탕으로 로그인 메서드를 다시 작성하면 다음과 같다.
쿠키를 생성하는 과정 대신, createSession 메서드를 통해 세션을 생성한다.
@PostMapping("/login")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
sessionManager.createSession(loginMember, response);
return "redirect:/";
}
브라우저에는 세션 데이터 접근을 위한 토큰이 쿠키로 저장된다.
로그인이 완료되고 homeLogin이 실행된다.
쿠키의 토큰을 사용해 서버의 세션 저장소에서 유저 정보를 조회하여 처리한다.
@GetMapping("/")
public String homeLogin(HttpServletRequest request, Model model) {
// 세션 관리자에 저장된 회원 정보 조회
Member loginMember = (Member)sessionManager.getSession(request);
if (loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
이렇게 세션을 만들지 않아도, Servlet에서 세션 기능을 제공한다.
서블릿을 통해 HttpSession을 생성하면, 쿠키 이름(name)은 JSESSIONID이고 값(value)은 랜덤 한 값이다.
- Tomcat이 지정하는 세션 ID에 대한 쿠키이름의 기본값이 JSESSIONID
세션을 생성하기 위해 HttpServletRequest의 getSession(boolean create)
을 사용한다.
- boolean create
- true(Default) → 기존 세션이 있다면 반환, 없다면 새로 생성해서 반환
- false → 기존 세션 반환, 없다면 null 반환
setAttribute(String name, Object value)
를 통해 세션에 데이터를 저장한다.
@PostMapping("/login")
public String login(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletRequest request) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
// 로그인 성공 처리
// 세션 생성
HttpSession session = request.getSession();
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
브라우저에 쿠키 저장 확인
세션 정보가 저장되고, homeLogin 메서드가 작동한다.
HttpSession을 가져와 getAttribute를 통해 사용자 정보를 가져온다.
@GetMapping("/")
public String homeLogin(HttpServletRequest request, Model model) {
HttpSession session = request.getSession(false);
if (session == null) {
return "home";
}
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
if (loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
스프링은 세션을 편리하게 사용할 수 있도록 @SessionAttribute
을 지원한다.
@GetMapping("/")
public String homeLoginV3Spring(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {
if (loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
서블릿에서 제공하는 세션은 시간만료 등을 알아서 처리해 주고, 옵션을 통해 임의로 조정도 가능하다.
- 💡 여기서 session id에 대해서 헷갈려서 추가로 찾아봤다.
- JSESSIONID값은 Tomcat에서 생성하는 값이다.
- 이 id를 Key로 갖고, 저장공간(session)을 value로 갖는 Map이 존재한다.
map → { key: session-id, value:Map }해당 value에 LOGIN_MEMBER을 key로, Member를 value로 가지는 Map이 저장되는 것이다.
'Spring 공부' 카테고리의 다른 글
ArgumentResolver (0) | 2023.12.19 |
---|---|
필터, 인터셉터 (0) | 2023.12.19 |
스프링 시큐리티 로그인 동작 따라가보기 (2) | 2023.09.25 |
빈 생명주기, 콜백 이용하기 (0) | 2022.08.16 |
Map, List에 빈 주입 & 사용하기 (0) | 2022.08.09 |