[LG CNS AM Inspire Camp] 16. Spring Security (with Session, Cookie, JWT)
1. Spring Security
Spring Security는 애플리케이션의 보안 기능을 제공하는 강력한 프레임워크이다. 인증(Authentication)과 권한 부여(Authorization)를 처리하는 데 사용되며, 쿠키, 세션, JWT 기반 인증을 모두 지원한다.
2. 쿠키(Cookie)와 세션(Session)
2.1. 쿠키(Cookie)
쿠키는 클라이언트(브라우저)에 저장되는 작은 데이터 조각으로, 서버가 사용자 정보를 저장하고 필요할 때 클라이언트에서 전송받을 수 있도록 한다.
✅ 쿠키 특징
- HTTP 요청 및 응답 헤더를 통해 주고받음
- 클라이언트(브라우저)에 저장됨
- 만료 시간이 지나거나 삭제될 때까지 유지됨
- 보안에 취약하므로 중요한 정보는 저장하면 안 됨
✅ 안전한 쿠키 관리 방법
- 중요한 정보를 포함하지 않도록 설정
- 만료 시간(Expires)과 지속 시간(Max-Age)을 최소한으로 설정
- HttpOnly 속성을 활성화하여 JavaScript에서 접근하지 못하도록 함
- Secure 속성을 활성화하여 HTTPS 통신에서만 전송하도록 제한
2.2. 세션(Session)
세션은 서버 측에서 관리되는 사용자 상태 저장 방식이다. 사용자가 로그인하면 세션 ID가 부여되고, 이후 요청마다 세션 ID를 이용해 인증된 사용자임을 확인할 수 있다.
✅ 세션 특징
- 서버에서 사용자 정보를 저장하고 관리
- 클라이언트는 세션 ID만 유지
- 서버 리소스를 소모하므로 관리가 필요함
✅ 안전한 세션 관리 방법
- 세션 ID 추측 방어: 예측 불가능한 값으로 생성해야 함
- 세션 고정(Fixation) 방어: 인증 후 새로운 세션 ID를 발급해야 함
- 세션 탈취 방어: HttpOnly, Secure 속성 적용
- 세션 타임아웃 설정 (application.properties)
- server.servlet.session.timeout=30m
- 다중 로그인 방어 (SecurityConfiguration.java)
- http.sessionManagement(auth -> auth .sessionFixation(ses -> ses.newSession()) .maximumSessions(1) .maxSessionsPreventsLogin(true));
3. JWT (JSON Web Token)
JWT는 서버에서 클라이언트의 인증 상태를 유지하기 위해 사용하는 토큰 기반 인증 방식이다. JWT는 쿠키나 세션과 달리 서버에서 상태를 저장하지 않고도 인증을 유지할 수 있다(Stateless).
jwt 토큰은 https://jwt.io/ 에서 내용을 뜯어볼 수도 있다.
✅ JWT 구조 JWT는 Header.Payload.Signature의 3가지 부분으로 구성된다.
- Header: 알고리즘과 타입 정보 포함
- Payload: 사용자 정보(클레임, Claim) 포함
- Signature: 변조 방지 및 보안 강화
✅ JWT 클레임(Claim) 종류
- iss: 토큰 발급자
- sub: 토큰 제목
- exp: 만료 시간
- iat: 발급 시간
- aud: 대상자 정보
3.1. JWT 생성
JWT는 사용자가 로그인할 때 생성되며, 이후 요청에서 Authorization 헤더를 통해 서버로 전달된다.
✅ JWT 생성 과정
- 사용자가 로그인하면 서버에서 JWT를 생성한다.
- 클라이언트는 JWT를 로컬 스토리지나 세션 스토리지에 저장한다.
- 이후 요청 시 Authorization 헤더에 JWT를 포함하여 서버에 전송한다.
String jwtToken = Jwts.builder()
.claim("name", userEntity.getName())
.claim("email", userEntity.getEmail())
.subject(userEntity.getUsername())
.id(String.valueOf(userEntity.getSeq()))
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(secretKey, Jwts.SIG.HS256)
.compact();
3.2. JWT 검증
JWT가 유효한지 확인하려면 서명(Signature)을 검증하고 만료 시간을 체크해야 한다.
public boolean validateToken(String token, UserEntity userEntity) {
if (isTokenExpired(token)) {
return false;
}
return getSubjectFromToken(token).equals(userEntity.getUsername());
}
3.3. JWT 필터 적용
서버는 클라이언트가 전송한 JWT를 검증하는 필터를 사용하여 보안 기능을 강화한다.
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserRepository userRepository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
String subject = jwtUtils.getSubjectFromToken(token);
UserEntity userEntity = userRepository.findByUsername(subject);
if (!jwtUtils.validateToken(token, userEntity)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
filterChain.doFilter(request, response);
}
}
4. Spring Security 설정
Spring Security를 활용하여 JWT 기반 인증을 적용할 수 있다.
✅ JWT를 사용하는 SecurityConfiguration 설정
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
5. 마무리
이번 강의를 통해 쿠키, 세션, JWT 기반 인증 방식의 차이점과 Spring Security에서 JWT를 활용하는 방법을 학습했다.
특히 Stateless 인증 방식인 JWT를 활용하여 확장성과 보안성을 높이는 방법을 이해했다.
Spring Security는 이 전에 프로젝트를 해오면서 여러번 다뤄본 프레임워크지만 보안과 직결된만큼 구조가 복잡하다. 완벽히 이해하려면 더 많이 뜯어보고 연습해야 하는 것 같다.
이 글은 LG CNS AM Inspire Camp 1기 진행 중 Obsidian에 정리해뒀던 글을 블로그에 옮긴 글입니다.