
쿠키는 사용자의 웹 브라우저에 저장되는 작은 데이터 조각으로, HTTP의 Stateless 특성을 보완하여 사용자 상태를 유지하는 데 사용됩니다.
HTTP는 기본적으로:
이러한 특성으로 인해 서버는 이전 요청에 대한 정보를 기억하지 못하므로, 로그인 상태 유지와 같은 기능을 위해 쿠키가 필요합니다.
쿠키 헤더:
쿠키 주요 속성:
Set-Cookie: sessionId=abcd; expires=Sat, 11-Dec-2024 00:00:00 GMT; path=/; domain=example.com; Secure; HttpOnly; SameSite=Strict
생명주기:
도메인(domain):
경로(path):
보안 설정:
로그인 상태 유지 과정:
1. 사용자 로그인 성공 시, 서버는 Set-Cookie 헤더로 쿠키 발급
2. 브라우저는 쿠키를 저장하고 이후 모든 요청에 Cookie 헤더로 포함
3. 서버는 쿠키 값으로 사용자 인증/인가 처리
// 로그인 성공 후 쿠키 설정
Cookie cookie = new Cookie("userId", String.valueOf(user.getId()));
cookie.setMaxAge(3600); // 1시간
cookie.setPath("/");
response.addCookie(cookie);
// 로그아웃 시 쿠키 삭제
Cookie cookie = new Cookie("userId", null);
cookie.setMaxAge(0);
response.addCookie(cookie);
보안 취약성:
해결 방법:
세션은 서버에서 사용자 상태 정보를 유지하는 방법으로, 쿠키의 보안 문제를 해결합니다.
세션 생성:
세션 사용:
Spring에서는 HttpSession을 사용해 세션을 쉽게 관리할 수 있습니다.
// 세션 생성 또는 조회
HttpSession session = request.getSession(); // 세션 없으면 생성
// 또는
HttpSession session = request.getSession(false); // 세션 없으면 null 반환
// 세션에 데이터 저장
session.setAttribute("loginUser", userDto);
// 세션에서 데이터 조회
UserDto loginUser = (UserDto) session.getAttribute("loginUser");
// 세션 무효화(로그아웃)
session.invalidate();
Spring MVC에서는 @SessionAttribute 애노테이션으로 더 간편하게 사용 가능:
@GetMapping("/home")
public String home(
@SessionAttribute(name = "loginUser", required = false) UserDto loginUser,
Model model
) {
// 로직 구현
}
# application.properties에서 세션 타임아웃 설정
server.servlet.session.timeout=1800 # 30분(초 단위)
토큰은 인증/인가 과정에서 사용되는 디지털 문자열로, 세션의 한계를 보완합니다.
토큰 사용 이유:
토큰 발급:
토큰 사용:
JWT는 가장 널리 사용되는 토큰 형식으로, 세 부분으로 구성됩니다:
xxxxx.yyyyy.zzzzz
Header.Payload.Signature
1. Header (헤더):
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload (페이로드):
{
"sub": "1234567890",
"name": "John Doe",
"exp": 1516239022
}
3. Signature (서명):
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
토큰 보안 강화를 위한 이중 토큰 구조:
1. Access Token:
2. Refresh Token:
인증 흐름:
1. 로그인 성공 → Access Token + Refresh Token 발급
2. API 호출 시 Access Token 사용
3. Access Token 만료 → Refresh Token으로 재발급 요청
4. 서버는 Refresh Token 유효성 검증 후 새 Access Token 발급
장점:
단점:
공통 관심사란 여러 위치에서 공통적으로 사용되는 부가 기능을 의미합니다.
인증/인가 처리와 같은 공통 관심사를 각 컨트롤러마다 구현하면:
이러한 문제를 해결하기 위해 Servlet Filter를 사용합니다.
Servlet Filter는 HTTP 요청과 응답을 가로채서 전처리/후처리하는 컴포넌트입니다.

Filter 인터페이스를 구현하여 필터를 만들 수 있습니다:
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {}
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
default void destroy() {}
}
주요 메서드:
로그인 체크 필터 구현:
@Slf4j
public class LoginCheckFilter implements Filter {
private static final String[] WHITE_LIST = {"/", "/login", "/logout", "/signup"};
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
log.info("인증 체크 필터 시작: {}", requestURI);
// 인증이 필요없는 URI인지 확인
if (!isLoginCheckPath(requestURI)) {
chain.doFilter(request, response);
return;
}
// 세션 확인
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute("loginUser") == null) {
log.info("미인증 사용자 요청: {}", requestURI);
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendRedirect("/login");
return;
}
// 인증 성공, 다음 필터 또는 서블릿 호출
chain.doFilter(request, response);
}
private boolean isLoginCheckPath(String requestURI) {
return !PatternMatchUtils.simpleMatch(WHITE_LIST, requestURI);
}
}
Configuration 클래스에서 필터 등록:
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
// 필터 설정
filterRegistrationBean.setFilter(new LoginCheckFilter());
// 필터 순서 설정
filterRegistrationBean.setOrder(1);
// URL 패턴 설정
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
여러 필터를 순서대로 적용할 수 있습니다:
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean loginCheckFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter());
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
1. 쿠키-세션 방식 선택 시:
2. 토큰 방식 선택 시:
3. 참고 사항:
쿠키(Cookie)
세션(Session)
토큰(Token)
Servlet Filter
인증과 인가