JWT (Json Web Token) 이란?
- JWT는 웹 애플리케이션에서 사용자 인증 및 정보 교환을 위한 토큰 기반 인증 방식입니다. JWT는 토큰 자체에 사용자의 클레임(claim)정보를 포함하고 있으며, 이를 통해 서버간의 신뢰성 있는 정보 교환을 가능하게 합니다.
- 서버의 상태를 유지하지 않고도(Stateless) 토큰을 사용하여 인증을 처리할 수 있습니다.
- 토큰을 사용하여 클라이언트와 서버간 인증을 할 수 있습니다.
Claim 이란?
클레임이란 사용자 정보나 데이터속성등을 의미한다. 클레임 기반 토큰은 토큰안에 정보를 담을 수 있는 특징이 있다.
클레임 기반 토큰은 아래예시와 같이 정보를 담고 있다. jwt는 클레임토큰중 가장 대표적인 것이다.
payload에서는 claim이라는 property를 key-value형태로 저장한다.
ex) { "sub": "12345" } 에서 sub 는 subject claim 이다.
Header + Payload + Signature
Header: Signature를 해싱하기 위한 알고리즘 정보들이 담겨있다.
Payload: 서버와 클라언트가 주고받는, 사스템에서 실제로 사용될 정보에 대한 내용들을 담고 있다.
Signature: 토큰의 유효성 검증을 위한 문자열. 이 문자열을 통해 서버에서는 이 토큰이 유효한 토큰인지를 검증할 수 있다.
JWT 장점
- 중앙의 인증서버, 데이터 스토어에 대한 의존성이 없다. 시스템 수평 확장에 유리.
- Base64 URL Safe Encoding을 사용 > URL, Cookie, Header 모두 사용 가능. 범용성이 좋음.
JWT 단점
- Payload의 정보가 많아지면 네트워크 사용량 증가, 데이터 설계 고려 필요.
- 토큰이 클라이언트에 저장되어 서버에서 클라인언트의 토큰을 조작할 수 없음.
sessionManagement(sessionManager -> sessionManager
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Jwt를 사용하기 때문에 session을 stateless로 설정한다.
stateless로 설정 시, Spring Security는 세션을 사용하지 않는다.
.csrf(AbstractHttpConfigurer::disable)
// API를 작성하는데 프런트가 정해져있지 않기 때문에 CSRF설정은 우선 꺼놓는다.
.httpBasic(AbstractHttpConfigurer::disable)
// httpBasic 방식 대신 Jwt를 사용하기 때문에 disable로 설정
.formLogin(AbstractHttpConfigurer::disable)
// formLogin 방식 대신 Jwt를 사용하기 때문에 disable로 설정
http
.cors(withDefaults()) // 1
@Value("${cors.allow-origins}")
private List<String> corsOrigins;
// 2
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOriginPatterns(corsOrigins);
corsConfiguration.setAllowedHeaders(List.of("*"));
corsConfiguration.setMaxAge(3600L);
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "PATCH"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
1번 부분을 먼저 보겠습니다. 저 곳에서 CORS 설정을 추가하는 것입니다. .cors(withDefaults())일 경우, corsConfigurationSource라는 이름으로 등록된 Bean을 이용합니다.
2번 부분으로 내려가보겠습니다. 이 곳은 CorsConfigurationSource Bean 생성을 통해 구체적인 CORS 정책을 설정하는 곳입니다.
.setAllowedOriginPatterns( )
: 허용할 도메인 목록. 출처(Origin)에 대해 스크립트 기반의 HTTP 통신을 허용하도록 설정합니다. 프론트가 따로 정해지지 않은 경우, *로 설정해 일단 모든 출처에 대해 허용하겠다는 의미를 내포하고 있다고 보면 됩니다.
.setAllowedHeaders(List.of("*"))
: 허용할 헤더 목록
.setMaxAge(3600L)
: preflight request 요청 결과를 캐시할 수 있는 시간을 나타냅니다. Preflight에 관한 내용은 아래 내용을 참고하자.
.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "PATCH"))
: 허용할 메서드 목록. 파라미터로 지정한 HTTP Method에 대한 HTTP 통신을 허용합니다.
Preflight란?
사전적의미 : 미리 보내는 것.
기본적으로 브라우저는 cross-origin 요청을 전송하기 전에 OPTIONS메소드로 preflight를 전송한다.
이 때, Response로 Access-Control-Allow-Origin과 Access-Control-Allow-Methods가 넘어오는데 이는
서버에서 어떤 origin과 어떤 method를 허용하는지 브라우저에게 알려주는 역할을 한다.
브라우저가 결과를 성공적으로 확인하고 나면 가능한 origin들로부터 cross-origin 요청을 보내서 그 이후 과정을 진행한다.
Preflight 관련 설정
그렇다면, cross-origin 요청을 보낼 때마다 preflight 요청 작업을 거쳐야 하는가? 그건 아니다.
서버 설정을 통해서 preflight 결과의 캐시를 일정 기간동안 저장시킬 수 있다.
이 캐시 정보가 살아있는 시간 동안은 preflight 를 생략하고 바로 요청 전송이 가능하다.
참고)
cors관련 설정을 포함한 필터.
기본적으로 서버 또는 지정된 특정 도메인의 요청만 허용하지만
프런트가 정해져있지 않기 때문에 모든 도메인을 허용하는 방식으로 설정.
*위의 예시가 프런트가 정해져 해당 도메인만을 허용하는 방식임.
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
.authorizeRequests( ) : HttpServletRequest를 사용하는 요청들에 대한 접근제한을 설정하겠다
.antMatchers(api path).permitAll( ) : "api path 주소"에 대한 요청은 인증없이 접근을 허용하겠다
.anyRequest( ).authenticated( ) : 나머지 요청들은 모두 인증되어야 한다
출처: https://jake-seo-dev.tistory.com/77 [제이크서 위키 블로그:티스토리]