기존 프로젝트를 스프링 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 버전 시큐리티 컨피그레이션 설정 끝!! :)
'프로젝트 > 토이 프로젝트) 오늘도 휴일' 카테고리의 다른 글
Service 를 테스트해서 오류를 잡아내다! (0) | 2023.07.01 |
---|---|
보안을 위해 닉네임 대신 id 검증으로 코드를 리팩토링 하자! (0) | 2023.06.22 |
NullCheck 를 @Valid 로 손쉽게 ^__^ (0) | 2023.06.22 |
좀 더 객체 지향에 걸맞게 메일 서비스를 수정해보자 ! (1) | 2023.06.13 |
토이 프로젝트 ) 휴일 팬사이트 "오늘도 휴일" : 사이트 소개 및 설명 (0) | 2023.05.26 |