Deprecated된 WebSecurityConfigurerAdapter 없이 CustomSecurityConfig 구현하기
In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration.
스프링시큐리티 5.7.0버전 이후로 WebSecurityConfigurerAdapter가 Deprecated되었습니다
참고하시라고 소스코드 올려봅니다.
참고로 예제소스는 남가람북스 구멍가게코딩단의 코드로배우는 웹 스프링부트 프로젝트 입니다.
AS-IS 소스
@Configuration
@Log4j2
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ClubUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin();
http.csrf().disable();
http.logout();
http.oauth2Login().successHandler(successHandler());
http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService); //7days
http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(apiLoginFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public ApiLoginFilter apiLoginFilter() throws Exception{
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login", jwtUtil());
apiLoginFilter.setAuthenticationManager(authenticationManager());
apiLoginFilter
.setAuthenticationFailureHandler(new ApiLoginFailHandler());
return apiLoginFilter;
}
@Bean
public JWTUtil jwtUtil() {
return new JWTUtil();
}
@Bean
public ApiCheckFilter apiCheckFilter() {
return new ApiCheckFilter("/notes/**/*", jwtUtil());
}
@Bean
public ClubLoginSuccessHandler successHandler() {
return new ClubLoginSuccessHandler(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin();
http.csrf().disable();
http.logout();
http.oauth2Login().successHandler(successHandler());
http.rememberMe().tokenValiditySeconds(60*60*7).userDetailsService(userDetailsService); //7days
http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(apiLoginFilter(), UsernamePasswordAuthenticationFilter.class);
}
configure 메서드는 공식 홈페이지에서 발표한대로 SecurityFilterChain를 리턴타입으로 받아서 그대로 작성하면 됩니다.


----------->
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin();
http.csrf().disable();
http.logout();
http.oauth2Login().successHandler(successHandler());
http.rememberMe().tokenValiditySeconds(60*60*7)
.userDetailsService(userDetailsService);
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public ApiLoginFilter apiLoginFilter() throws Exception {
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(authenticationManager());
return apiLoginFilter;
}
다만 authenticationManager()같은 메서드를 받아오는 경우엔 어떻게 작성을 해야할까요 ?
처음 삽질할땐 이런방식을 해봤습니다.
@Bean
public ApiLoginFilter apiLoginFilter(HttpSecurity http) throws Exception {
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
return apiLoginFilter;
}
private AuthenticationManager authenticationManager;
@Bean
public ApiLoginFilter apiLoginFilter() throws Exception {
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(authenticationManager);
return apiLoginFilter;
}
위 소스의 결과는
authenticationManager must be specified
이러한 에러가 나게됩니다.
-------------해결-----------------
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ....
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
를 filterChain으로 이동시켜주고 apiLoginFilter에 authenticationManger를 전달해줬습니다.
@Bean
public ApiLoginFilter apiLoginFilter(AuthenticationManager authenticationManager) throws Exception {
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(authenticationManager);
return apiLoginFilter;
}
apiLoginFilter에 파라미터로 받은 Authentication를 setAuthenticationManager에 주입시킵니다.
Description:
Parameter 0 of method apiLoginFilter in org.geon.club.config.SecurityConfig required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.
AuthenticationManager 가 SecurityConfig에 Bean으로 등록되어있지 않다는 에러로
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
AuthenticationManager를 Bean으로 등록시켜줍니다.
------------최종소스------------
TO-BE 소스
@Configuration
@Log4j2
@RequiredArgsConstructor
public class SecurityConfig {
private final ClubUserDetailService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin();
http.csrf().disable();
http.logout();
http.oauth2Login().successHandler(successHandler());
http.rememberMe().tokenValiditySeconds(60*60*7)
.userDetailsService(userDetailsService);
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(apiCheckFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(apiLoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public ClubLoginSuccessHandler successHandler() {
return new ClubLoginSuccessHandler(passwordEncoder());
}
@Bean
public ApiCheckFilter apiCheckFilter() {
return new ApiCheckFilter("/notes/**/*");
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public ApiLoginFilter apiLoginFilter(AuthenticationManager authenticationManager) throws Exception {
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(authenticationManager);
apiLoginFilter.setAuthenticationFailureHandler(new ApiLoginFailHandler());
return apiLoginFilter;
}
@Bean
public JWTUtil jwtUtil() {
return new JWTUtil();
}
참고자료
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter