쿠키, 토큰, 세션
- 쿠키 : 브라우저 저장공간. 사용자의 상태를 유지하기 위해 사용
- 만약 쿠키 없다면 매번 신분 증명 필요.
- 자동으로 요청 헤더에 담겨서 사용.
- 악의적인 사용자가 탈취 가능. 보안 약함.
- 토큰 : JWT
- JWT는 Base64로 인코딩된 것일 뿐, 암호화된 것이 아님. secretKey는 토큰의 내용이 변조되지 않았는지를 검증하는데 사용. 토큰의 내용은 숨기지 않는다.
- 그렇기에 중요 정보 담으면 안됨.
- 암호화 X, 변조 검증 O
- 세션
- 변조, 위조 불가능. 아무런 의미 없는 문자열.
- 실제 유저 정보는 세션 저장소(외부 db)에 저장됨.
- 사용자 실제 정보는 서버에 저장되어 있기 때문에 브라우저는 해당 정보 참조 가능한 sessionId 갖고 있음.
세션 직접 만들어 보기
[SessionData 클래스]
package com.standard.sparta.session;
import lombok.Getter;
@Getter
public class SessionData {
private final Long memberId; // 회원 아이디
private final String memberRole; // 회원 권한
private SessionData(Long memberId, String memberRole) {
this.memberId = memberId;
this.memberRole = memberRole;
}
/**
* 세션 데이터 생성
*/
public static SessionData createNewSessionData(Long memberId, String memberRole) {
return new SessionData(memberId, memberRole);
}
}
[Controller]
(원래는 비즈니스 로직은 서비스에 구현해야 하지만 간단하게 보여주기 위해 컨트롤러에서 구현함)
private final SessionRepository sessionRepository;
/**
* 로그인 - 세션 발급
*/
@PostMapping
public ResponseEntity<String> sessionLoginAPI() {
// 1. 로그인 로직
// 아이디, 비밀번호 검증
// 성공...
// 2. 생성 - 세션 데이터
Long memberId = 1L;
String memberRole = "ADMIN";
SessionData newSessionData = SessionData.createNewSessionData(memberId, memberRole);
// 3. 저장 - 세션 저장소에 유저 정보 저장
String sessionId = sessionRepository.save(newSessionData);
log.info("세션 아이디 : {}", sessionId);
// 4. 생성 - response 헤더 생성
String headerValue = "sessionId=" + sessionId;
HttpHeaders headers = new HttpHeaders();
headers.set("Set-Cookie", headerValue);
// 5. 응답 반환
return new ResponseEntity<>("로그인 성공", headers, HttpStatus.OK);
}
/**
* 인가가 필요한 API 접근 - 세션 조회
*/
@GetMapping
public ResponseEntity<String> sessionRequiredAPI(HttpServletRequest request) {
// 1. 조회 - 쿠키에서 sessionId 조회
Cookie cookie = findCookie(request).orElseThrow(() -> new RuntimeException("접근 권한이 없습니다."));
String sessionId = cookie.getValue();
// 2. 조회 - 세션 스토어에서 유저 정보 조회
SessionData sessionData = sessionRepository.getSession(sessionId)
.orElseThrow(() -> new RuntimeException("접근 권한이 없습니다."));
Long memberId = sessionData.getMemberId();
String memberRole = sessionData.getMemberRole();
// 검증 로직 및 비즈니스 로직...
String successStr = "접근 성공 - 회원 아이디 : " + memberId;
// 3. 응답 반환
return new ResponseEntity<>(successStr, HttpStatus.OK);
}
private Optional<Cookie> findCookie(HttpServletRequest request) {
if(request.getCookies() == null) {
return Optional.empty();
}
return Arrays.stream(request.getCookies())
.filter(cookie -> cookie.getName().equals("sessionId"))
.findAny();
}
[Repository]
package com.standard.sparta.session;
import org.springframework.stereotype.Repository;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Repository
public class SessionRepository {
// 세션 저장소는 redis 주로 사용. (메모리 기반 db)
// 세션은 서버 부하 높은데 메모리 기반은 입출력 굉장히 빠름.
private final Map<String, SessionData> sessionStore = new ConcurrentHashMap<>();
/**
* 세션 저장
*/
public String save(SessionData sessionData) {
// 1. 생성
String sessionId = UUID.randomUUID().toString();
// 2. 저장 - 유저 데이터를 세션 저장소에 저장
sessionStore.put(sessionId, sessionData);
return sessionId;
}
/**
* 세션 조회
*/
public Optional<SessionData> getSession(String sessionId) {
SessionData sessionData = sessionStore.get(sessionId);
return Optional.ofNullable(sessionData);
}
}
세션으로 구현할 수 있는 기능
- 세션 생성 기능 : 사용자가 로그인 할 때 세션 생성
- 세션 조회 기능 : 로그인된 사용자 식별과 상태를 확인할 때 사용
- 세션 자동 만료 기능 : 사용자의 활동이 일정시간 없을 때 사용 (세션 생성 시간 트래킹해서 해당 유저 세션 자동 만료)
- 세션 제거 기능 : 사용자가 로그아웃할 때 사용.
- 다중 세션 제거 기능 : 사용자가 로그인 되어 있는 모든 기기 로그아웃시 사용. 서버에서 저장하므로 로그인된 모든 기기 정보 서버가 갖고 있으므로 가능함.
- 세션 ID 자동 갱신 기능 : 사용자 세션 탈취/고정 공격 방어 위해 일정 주기마다 사용자의 세션 자동으로 갱신.
- 중복 세션 제어 기능 : 사용자가 여러 기기에서 로그인하지 못하도록 제어.
- 세션 로그 기능 : 사용자가 언제 어디서 어떻게 접근했는지 기록.
스프링에서 세션 활용해보기
스프링 부트에서는 HttpSession 인터페이스를 제공하여 편리하게 세션 기능을 사용할 수 있도록 해준다.
getSession() 호출되며 세션 관리자에게 세션 요청시킨다. (세션 관리자는 스프링 컨테이너 올라오며 같이 생성됨)
만약 세션이 없다면 새로 만들어주고, 있으면 기존 세션을 반환한다. (설정 가능, false로 설정하면 만들지 않음).
ManagerBase를 상속받고 있다.
HttpSession 구현체 사용해보기
HttpSession session = request.getSession();
// 세션 아이디 조회
String sessionid = session.getId();
// 세션 값 설정
session.setAttribute("memberId", memberId);
// 세션 값 조회
Long foundMemberId = (Long) session.getAttribute("memberId");
// 세션 값 제거
session.removeAttribute("memberId");
// 세션 삭제
session.invalidate();
// 세션 생성 시간 조회
long creationTime = session.getCreationTime();
// 세션 마지막 접근 시간 조회
long lastAccessedTime = session.getLastAccessedTime();
// 세션 유효 시간 설정(초단위로 설정: 10초)
session.setMaxInactiveInterval(10);
// 세션 유효 시간 조회
int maxInactiveInterval = session.getMaxInactiveInterval();
// 신규 세션 여부 확인
boolean aNew = session.isNew();
'내배캠 > TIL' 카테고리의 다른 글
Docker 설치 방법 및 오류 해결 (1) | 2024.09.27 |
---|---|
MyBatis 란? (1) | 2024.09.27 |
[SQL] 세션 변수 (0) | 2024.09.19 |
[SQL] 오프라인/온라인 판매 데이터 통합하기 (1) | 2024.09.14 |
트랜잭션 격리 수준 (0) | 2024.09.12 |