배경
사이드 프로젝트를 진행중에 프론트 님께서 연락이 왔다.
로그인할 때 nickname에 한글을 사용해서 가입했는데 깨져서 나온다고한다!
그런데 다른 api 요청에서는 한글이 잘 나온다..읭?
왜 로그인 요청에서만 한글이 깨지지??? araboza
원인
다른 api와 로그인 api의 차이점이 있다.
로그인 요청은 Security에서 동작한다. 그리고 로그인 요청은 Dispatcher Servlet을 거치기 전에 수행된다!
그래서 Encoding Filter를 거치지 못해서 한글이 깨지는 것이라고 볼 수 있다.
Encoding Filter란 Dispatcher Servlet 전에 설정한 인코딩으로 변환해서 컨트롤러에서 사용되게 해준다.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration {
...
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) throws Exception {
log.info("SecurityConfiguration.CustomFilterConfigurer.configure excute");
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
jwtTokenProvider, aes128Config, memberService, redisService);
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenProvider, redisService);
jwtAuthenticationFilter.setFilterProcessesUrl("/auth/login"); // login 요청 uri
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new LoginSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new LoginFailurHandler());
builder
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
}
해결 방법
Spring Security에서의 로그인 요청은 Encoding Filter를 거치지 않기 때문에 Spring Security에서도 UTF-8로 인코딩해주는 설정을 해줘야 한다.
CharacterEncodingFilter를 사용해서 UTF-8 인코딩으로 설정해줄 수 있다.
@Slf4j
@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration {
...
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@Override
public void configure(HttpSecurity builder) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter(); // 추가
filter.setEncoding("UTF-8"); // 추가
filter.setForceEncoding(true); // 추가
builder.addFilterBefore(filter, JwtAuthenticationFilter.class); // 추가
log.info("SecurityConfiguration.CustomFilterConfigurer.configure excute");
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
jwtTokenProvider, aes128Config, memberService, redisService);
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenProvider, redisService);
jwtAuthenticationFilter.setFilterProcessesUrl("/auth/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new LoginSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new LoginFailurHandler());
builder
.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
}
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
builder.addFilterBefore(filter, JwtAuthenticationFilter.class);
CharacterEncodingFilter 클래스를 생성한 후 UTF-8을 설정하고 강제로 인코딩하도록 설정했다.
Spring Security 이전에 Encoding이 되어야 하기 때문에 UsernamePasswordAuthenticationFilter를 상속 받은 JwtAuthenticationFilter 이전에 수행되도록 설정해줬다.
UsernamePasswordAuthenticationFilter는 Spring Security 가장 앞단에 시작하는 AuthenticationFilter를 구현한 클래스이다.
이제 잘 나오는지 PostMan으로 확인해보았다.
잘 나온다!