spring security apply() deprecated 이슈 트러블 슈팅
이번 트러블 슈팅은 스프링 시큐리티를 5에서 6.2로 마이그레이션하는 과정에서 발생한 이슈입니다.
이전 포스팅을 통해 스프링 시큐리티6가 체인 방식에서 람다식으로 바뀌었다고 소개했습니다. 이와 동시에 기존 방식인 체인방식에서 사용하던 메서드 일부가 다른 메소드로 대체되었는데요 대표척으로 apply()메서드 입니다.
저는 기존 코드에서 apply() 메서드를 커스터마이징한 JWT 필터를 적용하여 login 성공시에 암호화된 jwt 토큰을 반환하도록 구현하였는데요 스프링 시큐리티 6.2 버전에서는 apply() 메서드를 더이상 사용할 수 없게 되었습니다.
따라서 apply()메서드를 대체할 다른 메서드를 알아보았는데요, 그것은 바로 with()입니다.
@Configuration
@EnableWebSecurity
public class Config {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.with(MyCustomDsl.customDsl(), (dsl) -> dsl
.disable()
)
...;
return http.build();
}
}
public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder(this);
this.add(configurer);
customizer.customize(configurer);
return this;
}
스프링 시큐리티 공식문서와 실제 with()메서드를 확인해보니 다음과 같이 람다방식으로 메서드를 참조하는 것을 알 수 있습니다.
with() 메서드 적용
AS-IS
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers().frameOptions().sameOrigin()
.and()
.csrf().disable()
.cors()
.and()
.formLogin()
.loginPage("/auth/oauth")
.usernameParameter("email")
.passwordParameter("password")
.loginProcessingUrl("/social")
.defaultSuccessUrl("/")
.failureUrl("/auth/oauth")
.and()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(new UserAuthenticationEntryPoint())
.accessDeniedHandler(new UserAccessDeniedHandler())
.and()
.apply(new CustomFilterConfigurer()) // 커스텀 필터를 apply()메서드로 참조함
.and()
.authorizeHttpRequests(auth -> auth
.antMatchers(HttpMethod.GET, "/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/users/join","/auth/email","/auth/password").permitAll()
.antMatchers(HttpMethod.PATCH, "/auth/password").permitAll()
.antMatchers(HttpMethod.DELETE, "/user").hasRole("USER")
.antMatchers("/h2/**").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/oauth2/**", "/loading/**","/auth/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.successHandler(new OAuth2UserSuccessHandler(jwtTokenizer, userRepository,redisUtils)));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@CrossOrigin
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenizer, redisUtils);
jwtAuthenticationFilter.setFilterProcessesUrl("/api/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new UserAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new UserAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenizer, authorityUtils, redisUtils);
builder.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
TO-BE
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers((headerConfig) ->
headerConfig.frameOptions((HeadersConfigurer.FrameOptionsConfig::sameOrigin)
)
)
.cors(withDefaults())
.sessionManagement((sessionManagementConfig) ->
sessionManagementConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.with(new CustomFilterConfigurer(), CustomFilterConfigurer::build) // with() 메서드로 참조
.exceptionHandling((exceptionConfig) ->
exceptionConfig.authenticationEntryPoint(new UserAuthenticationEntryPoint()).accessDeniedHandler(new UserAccessDeniedHandler())
)
.authorizeHttpRequests((auth) -> auth
.requestMatchers(HttpMethod.GET, "/**").permitAll()
.requestMatchers(HttpMethod.POST, "api/oauth", "/api/login", "/api/users/join", "/auth/email", "/auth/password").permitAll()
.requestMatchers(HttpMethod.PATCH, "/auth/password").permitAll()
.requestMatchers(HttpMethod.DELETE, "/user").hasRole("USER")
.requestMatchers("/h2/**").permitAll()
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers("/loading/**", "/auth/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.successHandler(new OAuth2UserSuccessHandler(jwtTokenizer, userRepository, redisUtils))
.userInfoEndpoint(userInfoEndpointConfig ->
userInfoEndpointConfig.userService(oAuth2UserService)
)
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@CrossOrigin
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenizer, redisUtils);
jwtAuthenticationFilter.setFilterProcessesUrl("/api/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new UserAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new UserAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenizer, authorityUtils, redisUtils);
builder.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
public HttpSecurity build(){
return getBuilder();
}
}
'Project > Whistle(축구 매칭 웹 서비스)' 카테고리의 다른 글
Whistle, 휘슬 프로젝트 ERD 설명 (4) | 2024.06.25 |
---|---|
Spring Security 마이그레이션 중 발생한 에러 해결 (1) | 2024.05.22 |
[ whistle ] 불확실한 변수명과 클래스명의 위험성 (2) | 2023.12.05 |
[ Trouble Shooting ] match와 leagueList사이에서 순환참조가 발생함 (0) | 2023.09.21 |
[ Whistle ] Match 테이블과 Team의 다대다 연관 관계 필요성에 관련한 문제 (0) | 2023.09.14 |
spring security apply() deprecated 이슈 트러블 슈팅
이번 트러블 슈팅은 스프링 시큐리티를 5에서 6.2로 마이그레이션하는 과정에서 발생한 이슈입니다.
이전 포스팅을 통해 스프링 시큐리티6가 체인 방식에서 람다식으로 바뀌었다고 소개했습니다. 이와 동시에 기존 방식인 체인방식에서 사용하던 메서드 일부가 다른 메소드로 대체되었는데요 대표척으로 apply()메서드 입니다.
저는 기존 코드에서 apply() 메서드를 커스터마이징한 JWT 필터를 적용하여 login 성공시에 암호화된 jwt 토큰을 반환하도록 구현하였는데요 스프링 시큐리티 6.2 버전에서는 apply() 메서드를 더이상 사용할 수 없게 되었습니다.
따라서 apply()메서드를 대체할 다른 메서드를 알아보았는데요, 그것은 바로 with()입니다.
@Configuration
@EnableWebSecurity
public class Config {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.with(MyCustomDsl.customDsl(), (dsl) -> dsl
.disable()
)
...;
return http.build();
}
}
public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder(this);
this.add(configurer);
customizer.customize(configurer);
return this;
}
스프링 시큐리티 공식문서와 실제 with()메서드를 확인해보니 다음과 같이 람다방식으로 메서드를 참조하는 것을 알 수 있습니다.
with() 메서드 적용
AS-IS
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers().frameOptions().sameOrigin()
.and()
.csrf().disable()
.cors()
.and()
.formLogin()
.loginPage("/auth/oauth")
.usernameParameter("email")
.passwordParameter("password")
.loginProcessingUrl("/social")
.defaultSuccessUrl("/")
.failureUrl("/auth/oauth")
.and()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(new UserAuthenticationEntryPoint())
.accessDeniedHandler(new UserAccessDeniedHandler())
.and()
.apply(new CustomFilterConfigurer()) // 커스텀 필터를 apply()메서드로 참조함
.and()
.authorizeHttpRequests(auth -> auth
.antMatchers(HttpMethod.GET, "/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/users/join","/auth/email","/auth/password").permitAll()
.antMatchers(HttpMethod.PATCH, "/auth/password").permitAll()
.antMatchers(HttpMethod.DELETE, "/user").hasRole("USER")
.antMatchers("/h2/**").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/oauth2/**", "/loading/**","/auth/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.successHandler(new OAuth2UserSuccessHandler(jwtTokenizer, userRepository,redisUtils)));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@CrossOrigin
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenizer, redisUtils);
jwtAuthenticationFilter.setFilterProcessesUrl("/api/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new UserAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new UserAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenizer, authorityUtils, redisUtils);
builder.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
}
TO-BE
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers((headerConfig) ->
headerConfig.frameOptions((HeadersConfigurer.FrameOptionsConfig::sameOrigin)
)
)
.cors(withDefaults())
.sessionManagement((sessionManagementConfig) ->
sessionManagementConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.with(new CustomFilterConfigurer(), CustomFilterConfigurer::build) // with() 메서드로 참조
.exceptionHandling((exceptionConfig) ->
exceptionConfig.authenticationEntryPoint(new UserAuthenticationEntryPoint()).accessDeniedHandler(new UserAccessDeniedHandler())
)
.authorizeHttpRequests((auth) -> auth
.requestMatchers(HttpMethod.GET, "/**").permitAll()
.requestMatchers(HttpMethod.POST, "api/oauth", "/api/login", "/api/users/join", "/auth/email", "/auth/password").permitAll()
.requestMatchers(HttpMethod.PATCH, "/auth/password").permitAll()
.requestMatchers(HttpMethod.DELETE, "/user").hasRole("USER")
.requestMatchers("/h2/**").permitAll()
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers("/loading/**", "/auth/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.successHandler(new OAuth2UserSuccessHandler(jwtTokenizer, userRepository, redisUtils))
.userInfoEndpoint(userInfoEndpointConfig ->
userInfoEndpointConfig.userService(oAuth2UserService)
)
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity> {
@CrossOrigin
@Override
public void configure(HttpSecurity builder) throws Exception {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtTokenizer, redisUtils);
jwtAuthenticationFilter.setFilterProcessesUrl("/api/login");
jwtAuthenticationFilter.setAuthenticationSuccessHandler(new UserAuthenticationSuccessHandler());
jwtAuthenticationFilter.setAuthenticationFailureHandler(new UserAuthenticationFailureHandler());
JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtTokenizer, authorityUtils, redisUtils);
builder.addFilter(jwtAuthenticationFilter)
.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
}
public HttpSecurity build(){
return getBuilder();
}
}
'Project > Whistle(축구 매칭 웹 서비스)' 카테고리의 다른 글
Whistle, 휘슬 프로젝트 ERD 설명 (4) | 2024.06.25 |
---|---|
Spring Security 마이그레이션 중 발생한 에러 해결 (1) | 2024.05.22 |
[ whistle ] 불확실한 변수명과 클래스명의 위험성 (2) | 2023.12.05 |
[ Trouble Shooting ] match와 leagueList사이에서 순환참조가 발생함 (0) | 2023.09.21 |
[ Whistle ] Match 테이블과 Team의 다대다 연관 관계 필요성에 관련한 문제 (0) | 2023.09.14 |