JwtAuthenticationFilter는 Spring Security의 필터 중 하나로, 클라이언트의 요청이나 서버의 응답을 가로채어서 JWT 토큰을 확인하고 인증을 처리하는 역할을 합니다. 클라이언트가 요청을 보낼 때 해당 필터를 거쳐 JWT 토큰을 검증하고, 서버가 응답을 할 때도 해당 필터를 통해 JWT 토큰을 생성하고 응답에 포함시킬 수 있습니다. 이를 통해 보안 및 권한 검사를 수행하고, 사용자 인증 및 권한 부여를 처리할 수 있습니다.
public class JwtAuthenticationFilter extends OncePerRequestFilter
OncePerRequestFilter
OncePerRequestFilter는 Spring Security에서 제공하는 필터 중 하나로, 각 요청당 한 번만 실행되도록 보장하는 필터입니다. 이 필터는 서블릿 필터의 추상 클래스인 GenericFilterBean을 확장하며, doFilterInternal 메서드를 구현하여 필터링 로직을 정의합니다.
OncePerRequestFilter는 보통 Spring Security에서 인증, 권한 부여, 요청 로깅 등과 같은 작업을 수행하는 데 사용됩니다. 요청이 필터 체인을 따라 흐를 때 한 번만 실행되어야 하는 경우에 이 필터를 사용하는 것이 유용합니다.
OncePerRequestFilter 클래스는 GenericFilterBean을 확장하고 있으며, doFilterInternal 메서드를 오버라이드하여 필터링 로직을 정의합니다. 따라서 개발자는 OncePerRequestFilter를 상속받아 doFilterInternal 메서드를 구현하여 원하는 필터 동작을 지정할 수 있습니다. doFilterInternal 메서드는 각 요청에 대해 한 번 호출되며, 이를 통해 요청을 필터링하고 원하는 처리를 수행할 수 있습니다.
서블릿 컨테이너로부터 요청을 받거나 응답을 보내고, 필터체인 내에서 다음 필터로 요청을 전달하는 역할을 수행 합니다.
FilterChain
필터 체인은 웹 애플리케이션에서 요청을 받고 응답을 보내는 과정 중에 여러 필터가 연속적으로 적용되는 메커니즘을 말합니다. 각각의 필터는 요청을 가로채어 원하는 작업을 수행한 뒤, 다음 필터로 요청을 전달하거나 응답을 수정하여 다음 단계로 전달합니다. 필터 체인은 보안, 로깅, 인코딩 변환 등과 같은 다양한 작업을 수행하기 위해 사용됩니다.
각각의 체인은 필터를 의미하고, 해당 필터에선 HTTP 의 요청에 대한 특정 작업을 수행합니다. 해당 작업이 끝나면 다음 체인으로 연결을 시켜줘 작업을 이행할 수 있게 합니다.
doFilterIntenal 에서 사용 될 resolveTokenFromRequest 메서드
(userDetailsService를 구현한 AuthenticationService 에서 Override 구현 돼 있다.)
public class AuthenticationService implements UserDetailsService {
private final AccountRepository accountRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.accountRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("해당 유저는 존재하지 않습니다."));
}
}
GPT 한테 물어보니 보안상의 이유로 signWith(SignatureAlgorithm, Key) 요 메서드는 잘 사용 되지 않는 단다.
보니까 그럼 다른 게, Key key 요렇게 Key를 넣으란다.
그래서
private static final Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
요래 전역변수를 만들어 놓고,
.signWith(secretKey)
요래 바꿔서 사용 한다.
Key 란 ?
Key는 Java Cryptography Architecture (JCA)에서 사용되는 인터페이스입니다. 이 인터페이스는 비밀 키, 공개 키 및 시크릿 키와 같은 암호화 및 서명에 사용되는 키를 표현합니다.
Keys.secretKeyFor(SignatureAlgorithm.HS512)는 주어진 알고리즘 (여기서는 HS512)에 따라 무작위 비밀 키를 생성하는 메서드입니다. 이 메서드는 암호화 및 서명 작업에 사용되는 키를 생성합니다. 여기서 HS512는 HMAC-SHA512 알고리즘을 나타냅니다. HMAC은 해시 기반 메시지 인증 코드로, 메시지 무결성을 보호하기 위해 사용됩니다.
따라서 Keys.secretKeyFor(SignatureAlgorithm.HS512)는 HMAC-SHA512 알고리즘에 대한 비밀 키를 생성하고 이를 Key 인터페이스로 반환합니다. 이것은 일반적으로 JWT의 서명 작업에 사용되는 비밀 키입니다.
그러니까
signWith(SignatureAlgorithm.HS512, key) 와 같은 방식의 메서드는 어찌됐건 key를 숨겨둬야 하는데 (지금 실습에선 yml에 jwt/secretkey 에 base64 를 활용한 비밀키를 넣어뒀다) key가 노출 될 위험이 있으니 더 안전하고 간단한 signWith(Key) 메서드를 사용 하라는 것 같다.
JWT(Json Web Token)는 비밀 키를 사용하여 서명(signature)을 생성합니다. 이 서명은 JWT의 무결성을 보장하고 JWT가 변경되지 않았음을 검증하는 데 사용됩니다. 비밀 키는 이 서명을 생성하고 검증하는 데 사용되는 중요한 보안 요소입니다.
일반적으로 비밀 키는 base64로 인코딩됩니다. 그 이유는 다음과 같습니다:
Binary Data Representation: 비밀 키는 바이너리 데이터로서, 문자열로 직접 표현할 수 없습니다. 따라서 바이너리 데이터를 텍스트로 표현할 수 있는 base64 인코딩을 사용하여 키를 문자열로 변환합니다.
ASCII 문자 사용: base64는 ASCII 문자 집합을 사용하여 데이터를 표현하므로, 키를 표현하는 데 사용하기 적합합니다.
문자열 처리 편의성: base64로 인코딩된 문자열은 일반적인 문자열 처리 도구를 사용하여 쉽게 다룰 수 있습니다.
안정성과 이식성: base64는 데이터를 안전하게 전송하고 저장하는 데 사용되는 표준 방법입니다. 이식성이 뛰어나며, 다양한 시스템과 프로그래밍 언어에서 사용할 수 있습니다.
따라서 JWT를 구현할 때 비밀 키를 base64로 인코딩하여 사용하는 것은 일반적인 관행입니다. 이는 안전한 보안 키를 문자열로 표현하고 사용하기 위한 표준적인 방법입니다.
base64 란?
base64는 이진 데이터를 텍스트로 인코딩하기 위한 인코딩 방식 중 하나입니다. 이진 데이터는 일반적으로 바이너리 형태로 저장되어 있는데, 이를 텍스트 형태로 전환할 때 사용됩니다. 이 방식은 이진 데이터를 ASCII 문자 집합에 속하는 문자들로만 이루어진 텍스트 형태로 변환하여 텍스트 기반 시스템에서도 사용할 수 있도록 해줍니다.
base64 인코딩은 다음과 같은 특징을 갖습니다:
문자 집합: base64 인코딩은 64개의 문자로 이루어진 집합을 사용합니다. 이 문자 집합에는 대문자(A-Z), 소문자(a-z), 숫자(0-9), 그리고 두 개의 추가 문자(+와 /)가 포함되어 있습니다. 이렇게 총 64개의 문자로 이루어진 집합을 사용하기 때문에 base64라고 불립니다.
3바이트 묶음: base64 인코딩은 입력 데이터를 3바이트씩 묶어서 처리합니다. 각각의 3바이트는 총 24비트를 나타내는데, 이 비트들을 6비트씩 나누어서 base64 문자 집합에 매핑됩니다.
패딩: 입력 데이터의 바이트 수가 3의 배수가 아닌 경우, 패딩 문자인 '='가 추가됩니다. 이는 base64 인코딩된 데이터의 길이가 항상 4의 배수가 되도록 보장합니다.
텍스트로의 변환: base64 인코딩은 이진 데이터를 텍스트 형태로 변환합니다. 이진 데이터는 주로 바이너리 파일이나 이미지 파일과 같은 형태로 저장되어 있으며, 이를 텍스트 형태로 변환하여 전송하거나 저장할 수 있게 합니다.
base64는 이진 데이터를 텍스트로 변환할 때 주로 사용되며, 이메일과 웹에서 이미지 파일이나 바이너리 파일을 전송하거나 저장할 때 자주 활용됩니다. 또한 암호학에서는 주로 인증 정보나 디지털 서명과 같은 데이터를 안전하게 전송하거나 저장할 때 base64 인코딩을 사용합니다.