본문 바로가기
프로젝트/토이 프로젝트) 오늘도 휴일

Spring Security ) Spring 3.0 에 맞춰 SecurityConfig 를 수정하자!

by 휴일이 2023. 6. 14.

 

 

기존 프로젝트를 스프링 3.0 으로 마이그레이션하려고 결심 후

기존 코드를 몇가지 수정해줘야 했는데

그 중 SecurityConfig 설정이 좀 달라져서 몇 가지 수정해주었다

 

 

 

스프링 2.0 까지는 WebSecurityConfigureAdapter 클래스를 상속받아 구현했는데

3.0 에서는 해당 클래스를 지원하지 않아서

SecurityConfig 에서 사용하던 코드들을

일부 수정해야한다

 

 

기존 코드

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigureAdapter {

    private final WebService webService;
    private final MemberJoinService memberJoinService;
    private final JwtTokenParser jwtTokenParser;
    private final JwtTokenService jwtTokenService;
    private final CustomDefaultOAuth2UserService customDefaultOAuth2UserService;
    private final CustomOAuth2AuthorizationRequestResolver customOAuth2AuthorizationRequestResolver;
    private final ClientRegistrationRepository clientRegistrationRepository;
    private final OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository;
    private final SnsInfo snsInfo;
    private final KakaoJwk kakaoJwk;
    private final GoogleJwk googleJwk;
    
    public SecurityConfig(WebService webService, MemberJoinService memberJoinService, JwtTokenParser jwtTokenParser, JwtTokenService jwtTokenService, CustomDefaultOAuth2UserService customDefaultOAuth2UserService, CustomOAuth2AuthorizationRequestResolver customOAuth2AuthorizationRequestResolver, ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository, SnsInfo snsInfo, KakaoJwk kakaoJwk, GoogleJwk googleJwk) {
        this.webService = webService;
        this.memberJoinService = memberJoinService;
        this.jwtTokenParser = jwtTokenParser;
        this.jwtTokenService = jwtTokenService;
        this.customDefaultOAuth2UserService = customDefaultOAuth2UserService;
        this.customOAuth2AuthorizationRequestResolver = customOAuth2AuthorizationRequestResolver;
        this.clientRegistrationRepository = clientRegistrationRepository;
        this.oAuth2AuthorizedClientRepository = oAuth2AuthorizedClientRepository;
        this.snsInfo = snsInfo;
        this.kakaoJwk = kakaoJwk;
        this.googleJwk = googleJwk;
    }

    @Override
    AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Override
    AuthenticationConfiguration authenticationConfiguration() {
        return new AuthenticationConfiguration();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http

                .csrf()
                .disable()

                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .authorizeRequests()
                //, "/fanLetter/remove/**"
                .mvcMatchers("/fanLetter/write", "/fanLetter/modify/**",
                        "/fanLetter/remove/**",
                        "/market/buy/write", "/market/buy/modify/**",
                        "/market/buy/comment/write", "/market/buy/comment/remove/**",
                        "/market/sell/write")
                .hasRole("USER")
                .anyRequest()
                .permitAll()

                .and()
                .addFilterBefore(new JwtFilter(userDetailsService(), jwtTokenParser, jwtTokenService), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(new JwtTokenSetFilter(), UsernamePasswordAuthenticationFilter.class)
                // OAuth2
                .addFilterBefore(new CustomOAuth2AuthorizationCodeGrantFilter(clientRegistrationRepository, oAuth2AuthorizedClientRepository, authenticationManager(authenticationConfiguration()), customDefaultOAuth2UserService, snsInfo), OAuth2LoginAuthenticationFilter.class)
                .addFilterAfter(new OAuth2JwtTokenFilter(webService, jwtTokenService, jwtTokenParser, memberJoinService, snsInfo, kakaoJwk, googleJwk), OAuth2LoginAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) // 인증이 실패했을 경우
                .accessDeniedHandler(new CustomAccessDeniedHandler()) // 권한이 없을 경우

                .and()
                .oauth2Login()
                .loginPage("/loginForm")
                .authorizationEndpoint()
                .authorizationRequestResolver(customOAuth2AuthorizationRequestResolver)
                .and()
                .userInfoEndpoint()
                .userService(customDefaultOAuth2UserService) // 로그인
                .and()
                .successHandler(new CustomOAuth2SuccessHandler(jwtTokenService, memberJoinService))

                .and()
                .logout()
                .logoutSuccessUrl("/")
                .permitAll()
        ;
    }

    @Override
    public WebSecurityCustomizer configure() throws Exception {
        return (web) -> web
                .ignoring()
                .mvcMatchers("/static/**", "/favicon.ico");
    }


}

 

기존 코드는 이런 식으로  상속 받은 클래스의 코드를 오버라이딩했지만

이제는 빈으로 등록해서 정말 설정 그 자체로 쓰면 됨..

 

 

 

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public AuthenticationConfiguration authenticationConfiguration() {
        return new AuthenticationConfiguration();
    }

 

authenticationManager 에 UserDetailsSevice나 PasswordEncoder 를 넣지 않아도 얘가 자동으로 알아먹는다고 한다

 

나는 Jwt 때문에 직접 필터를 설정해줘서 유저디테일즈서비스와 패스워드인코더를 사용하는 코드를 직접 구현했기 때문에

매니저 설정을 따로 하지 않았지만

세션 로그인을 해서 스프링 시큐리티가 동작할 패스워드인코더/유저디테일즈서비스 를 설정해야한다면

이젠 따로 등록을 안 해도 된다는 것 !!ㅋ_ㅋ

 

다만 나는 유저디테일즈서비스를 사용하는 필터에 매개변수를 줘야해서...선언은 해야했음

 

 

 

기존 configure 코드

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http

                .csrf()
                .disable()

                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

                .and()
                .authorizeRequests()
                //, "/fanLetter/remove/**"
                .mvcMatchers("/fanLetter/write", "/fanLetter/modify/**",
                        "/fanLetter/remove/**",
                        "/market/buy/write", "/market/buy/modify/**",
                        "/market/buy/comment/write", "/market/buy/comment/remove/**",
                        "/market/sell/write")
                .hasRole("USER")
                .anyRequest()
                .permitAll()

                .and()
                .addFilterBefore(new JwtFilter(userDetailsService(), jwtTokenParser, jwtTokenService), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(new JwtTokenSetFilter(), UsernamePasswordAuthenticationFilter.class)
                // OAuth2
                .addFilterBefore(new CustomOAuth2AuthorizationCodeGrantFilter(clientRegistrationRepository, oAuth2AuthorizedClientRepository, authenticationManager(authenticationConfiguration()), customDefaultOAuth2UserService, snsInfo), OAuth2LoginAuthenticationFilter.class)
                .addFilterAfter(new OAuth2JwtTokenFilter(webService, jwtTokenService, jwtTokenParser, memberJoinService, snsInfo, kakaoJwk, googleJwk), OAuth2LoginAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) // 인증이 실패했을 경우
                .accessDeniedHandler(new CustomAccessDeniedHandler()) // 권한이 없을 경우

                .and()
                .oauth2Login()
                .loginPage("/loginForm")
                .authorizationEndpoint()
                .authorizationRequestResolver(customOAuth2AuthorizationRequestResolver)
                .and()
                .userInfoEndpoint()
                .userService(customDefaultOAuth2UserService) // 로그인
                .and()
                .successHandler(new CustomOAuth2SuccessHandler(jwtTokenService, memberJoinService))

                .and()
                .logout()
                .logoutSuccessUrl("/")
                .permitAll()
        ;
    }

 

 

 

이렇게 바꾸면 된다

 

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())

                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(request -> request.requestMatchers(
                        "/fanLetter/write", "/fanLetter/modify/**",
                        "/fanLetter/remove/**",
                        "/market/buy/write", "/market/buy/modify/**",
                        "/market/buy/comment/write", "/market/buy/comment/remove/**",
                        "/market/sell/write"
                ).hasRole("USER")
                        .anyRequest().permitAll())

                .addFilterBefore(new JwtFilter(userDetailsService(), jwtTokenParser, jwtTokenService), UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(new JwtTokenSetFilter(), UsernamePasswordAuthenticationFilter.class)
                // OAuth2
                .addFilterBefore(new CustomOAuth2AuthorizationCodeGrantFilter(clientRegistrationRepository, oAuth2AuthorizedClientRepository, authenticationManager(authenticationConfiguration()), customDefaultOAuth2UserService, snsInfo), OAuth2LoginAuthenticationFilter.class)
                .addFilterAfter(new OAuth2JwtTokenFilter(webService, jwtTokenService, jwtTokenParser, memberJoinService, snsInfo, kakaoJwk, googleJwk), OAuth2LoginAuthenticationFilter.class)
                .exceptionHandling(exception -> exception.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                        .accessDeniedHandler(new CustomAccessDeniedHandler()))

                .oauth2Login(oauth -> oauth.loginPage("/loginForm")
                        .authorizationEndpoint(end -> end.authorizationRequestResolver(customOAuth2AuthorizationRequestResolver))
                        .userInfoEndpoint(userInfo -> userInfo.userService(customDefaultOAuth2UserService))
                        .successHandler(new CustomOAuth2SuccessHandler(jwtTokenService, memberJoinService)))

                .logout(logout -> logout.logoutSuccessUrl("/").permitAll())
                .build();
    }

 

 

사실 하다보면 생각보다 간단한데

이전에는 .and() 이런 메서드를 사용하거나

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

이런 식으로 직접 이어서 설정해줬다면

 

.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

이제는 sessionManagement() 같은 설정 메서드 자체가 매개변수 @NotNull 이 되고

그 안에 직접 커스텀해서 설정을 해야되는데,

이너클래스로 해도 좋겠지만 우리에겐 람다식이 있으니까 손쉽게 람다로 해결한다!

 

쉽게 생각하면

 

휴일().

.외모().키가큼().손이큼()

.그리고().성격().조용함().재밌음()

 

이전에는 이렇게 나열하듯이 선언했다면

바뀐 방법은

 

휴일().

.외모( 외모 -> 외모.키가큼().손이큼() )

.성격 ( 성격 -> 성격.조용함().재밌음() )

 

요렇게 설정을 안으로 집어넣어서 정리하는 느낌?ㅎㅎ

 

뭐 어쨌든 그냥 모든 설정을 이런식으로 바꾸기만 하면 된다...ㅋㅋ(하다보니 알았음)

 

 

.authorizeRequests() -> .authorizeHttpRequests()

참고로 해당 메서드는 이렇게 바뀌었으니 참고할것 ^__^

(mvcMathers() 도 그냥 requestMathers()로 통일하면 되는 모양이다)

 

 

    @Bean
    public WebSecurityCustomizer configure() throws Exception {
        return (web) -> web
                .ignoring()
                .requestMatchers("/static/**", "/favicon.ico");
    }

 

그러니 이것도 이렇게 requestMathers 로 바꿔준다!

 

 

 

이러면 스프링 3.0 버전 시큐리티 컨피그레이션 설정 끝!! :)

728x90