JwtRequestFilter란?
JwtRequestFilter는 JWT 토큰을 이용해 사용자를 인증하는 스프링 시큐리티 필터
클라이언트가 서버에 API 요청을 보낼 때마다 이 필터가 실행되어 Authorization 헤더에 포함된 JWT 토큰을 검증
JwtRequestFilter는 클라이언트가 제공한 토큰이 유효한지 확인하고, 유효하다면 사용자의 인증 정보를 SecurityContext에 저장하여 인증 상태를 유지한다.
JWT 토큰 인증 과정
1. 요청의 Authorization 헤더에서 JWT 토큰 추출
final String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7); // "Bearer " 이후의 실제 JWT 토큰 값 추출
}
• request.getHeader("Authorization")을 통해 클라이언트 요청 헤더에서 Authorization 값(token)을 가져온다.
• Bearer 로 시작하는지 확인한 후, JWT 토큰을 추출.
* Bearer 뒤에 위치한 부분이 실제 JWT 토큰 값
🔍 Bearer가 뭘까?
일반적으로 토큰은 요청 헤더의 Authorization 필드에 담아져 보내진다.
토큰에는 많은 종류가 있고 서버는 여러 종류의 토큰을 처리하기 위해 type에 따라 토큰을 처리한다.
Authorization: <type> <credentials> ⇦ 이런 형식으로 담아져 보내짐
Bearer은 <type>에 해당하고, JWT 혹은 OAuth에 대한 토큰에 사용한다.
다른 인증 타입
Basic : 사용자 아이디와 암호를 Base64로 인코딩한 값을 토큰으로 사용
Bearer : JWT 혹은 OAuth에 대한 토큰을 사용
Digest : 서버에서 난수 데이터 문자열을 클라이언트에 보내고, 클라이언트는 사용자 정보와 nonce를 포함하는 해시값을 사용하여 응답
HOBA : 전자 서명 기반 인증
Mutual : 암호를 이용한 클라이언트-서버 상호 인증
2. JWT 토큰에서 사용자 이름(이메일) 추출
username = jwtService.extractUsername(jwt); // 토큰에서 사용자 이름(이메일) 추출
• jwtService.extractUsername(jwt) 메소드를 호출하여, JWT 토큰의 페이로드에서 사용자 이름(이메일)을 추출한다.
• JWT 토큰이 만료되지 않았다면, 여기서 정상적인 사용자 정보를 얻을 수 있음
3. SecurityContext에 인증 정보가 없는지 확인
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 아래 단계로 인증 시도
}
• SecurityContextHolder.getContext().getAuthentication()을 호출해서 현재 요청에서 이미 인증 정보가 설정되어 있는지를 확인
• 인증 정보가 없으면, JWT 토큰을 검증하고 인증 정보를 저장하기 위한 로직 실행
4. JWT 토큰 유효성 검증
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtService.validateToken(jwt, userDetails.getUsername())) {
// 인증 성공
}
• UserDetailsServiceImpl.loadUserByUsername(username)을 호출해, 데이터베이스에서 해당 사용자의 세부 정보를 로드함
• jwtService.validateToken(jwt, userDetails.getUsername())으로 JWT 토큰의 유효성을 검증한다.
토큰이 만료되지 않고, Username이 같다면 해당 토큰을 통해 인증성공!
5. SecurityContext에 인증 정보 설정
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
UsernamePasswordAuthenticationToken(인증된 사용자 정보, 인증 자격 증명, 권한 정보)
스프링 시큐리티에서 인증 객체를 표현하는 클래스
첫번째 인자: 인증된 사용자 정보
주로 사용자 이름이나 userDetails를 담는다.
두번째 인자: 사용자의 인증 자격 증명
JWT 토큰 인증 방식에서는 이미 인증이 완료된 상태에서 이 객체를 생성하기 때문에, 비밀번호는 필요 없다.
세번째 인자: 권한 정보
주로 사용자의 역할(Role) 정보
WebAuthenticationDetailsSoutce
요청에서 추가적인 정보를 추출해 인증 객체에 담는다.
예를 들어, IP 주소나 세션 ID와 같은 정보를 추출해 Authentication 객체에 포함할 수 있다.
SecurityContextHolder
스프링 시큐리티에서 인증 정보를 저장하는 Context
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken)
앞서 생성된 인증 객체를 SecurityContext에 저장하여, 이 요청 내에서 인증된 상태로 처리
요청이 끝나면 인증상태 초기화함.
• JWT 토큰이 유효하다면, UsernamePasswordAuthenticationToken 객체를 생성하여 현재 요청에 대한 인증 정보로 설정합니다.
• 이 정보는 SecurityContextHolder에 저장되며, 해당 요청이 처리되는 동안에만 유지됩니다.
6. 필터 체인 내 다음 필터로 요청 전달
chain.doFilter(request, response);
• 필터 체인의 다음 필터로 요청을 전달합니다. 이후 요청은 인증된 상태에서 처리됩니다.
요약
JwtRequestFilter는 클라이언트 요청에서 JWT 토큰을 추출하고,
토큰의 유효성을 검증한 뒤,
유효한 경우 사용자의 인증 정보를 설정하여
SecurityContext에서 인증된 상태로 요청을 처리한다!
'[ STUDY ] > Spring Boot' 카테고리의 다른 글
[ JWT를 활용한 Spring Security ] Access Token 재발급 (0) | 2024.09.25 |
---|---|
[ JWT를 활용한 Spring Security ] Access Token, Refresh Token 발급 및 저장 (0) | 2024.09.25 |
[ JWT를 활용한 Spring Security ] Refresh Token?Access Token? (0) | 2024.09.20 |
[ JWT를 활용한 Spring Security ] JWT 토큰 생성 (0) | 2024.09.20 |
[ JWT를 활용한 Spring Security ] 파일 설명 (1) | 2024.09.13 |