
네이버 로그인 구현을 하려고 하면서 가장 난항을 겪었던 부분이다.
어떤식으로 하는 느낌이다라는 것만 인지하고 있었고 직접 제대로 구현을 해보고 이해를 하려고 했던 것은 처음이었고, 게다가 SNS 로그인까지 연동이 되다보니 좀 더 여려웠던 것 같았다.
그래서 좀 더 직접적인 샘플코드와 설명을 필요로 했는데 서치를 하다가 아래의 블로그를 찾아서 관련된 코드를 작성하는데 좀 더 수월했다.
https://wildeveloperetrain.tistory.com/252
해당 블로그에는 깃헙도 나와있어서, 깃헙의 코드를 베이스로 세팅을 해주고 필요한 부분들은 커스터마이징을 해주었다.
그 중에서도 security의 메인이 되는 config 설정을 보면 아래와 같이 세팅을 해 주었다.
public class WebSecurityConfigure {
private final CustomOAuth2UserService customOAuth2UserService;
private final JwtTokenProvider jwtTokenProvider;
private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
private final CustomUserDetailsService customUserDetailsService;
@Value("${url.frontend}")
private String FRONTEND_BASE_URL;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.debug("filterChain => ");
log.debug(" {}", http.toString());
//httpBasic, csrf, formLogin, rememberMe, logout, session disable
http
// .cors()
// .and()
.csrf().disable()
.formLogin().disable()
.rememberMe().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//요청에 대한 권한 설정
http.authorizeRequests()
.antMatchers("/","/api-docs/**","/api/**","/api/swagger-ui/index.html", "/api/swagger.html","/js/**","/css/**").permitAll()
.anyRequest().authenticated();
//oauth2Login
http.oauth2Login()
.authorizationEndpoint()
.baseUri("/api/oauth2/authorize") // 소셜 로그인 url
.authorizationRequestRepository(cookieAuthorizationRequestRepository) // 인증 요청을 cookie 에 저장
.and()
.redirectionEndpoint()
.baseUri("/api/oauth2/callback/*") // 소셜 인증 후 redirect url
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService) // 회원 정보 처리
//userService()는 OAuth2 인증 과정에서 Authentication 생성에 필요한 OAuth2User 를 반환하는 클래스를 지정한다.
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
http.logout()
.logoutUrl("/api/member/logout")
.deleteCookies("JSESSIONID")
.clearAuthentication(true)
.logoutSuccessHandler((request, response, authentication) ->
response.sendRedirect(FRONTEND_BASE_URL)
);
//jwt filter 설정
http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
위와 같이 세팅을 해 주었다.
(+ 2023.11.07 추가 사항)
기존에는 spring boot 2.7.14 를 사용했었지만, 프로젝트를 진행하면서 spring boot를 3.1.4로 사용하면서 spring security가 spring boot 3점대부터 6점대로 변경되었다.
그러면서 기존에 사용하던 .disable을 사용하지 않고, 람다 함수를 사용하도록 변경되었다.
public class WebSecurityConfigure {
private final CustomOAuth2UserService customOAuth2UserService;
private final JwtTokenProvider jwtTokenProvider;
private final CookieAuthorizationRequestRepository cookieAuthorizationRequestRepository;
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
private final CustomUserDetailsService customUserDetailsService;
@Value("${url.frontend}")
private String FRONTEND_BASE_URL;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// CORS 관련 설정
@Bean
public CorsConfigurationSource configurationSource () {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(false);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setMaxAge(3600 * 6L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.debug("filterChain => ");
log.debug(" {}", http.toString());
// 기타 보안 설정
// 람다 함수 및 추상 메소드 사용
http
.cors(cors -> cors.configurationSource(configurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.rememberMe(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
//요청에 대한 권한 설정
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/member/login", "/api/member/join", "/api/game/file/game-download", "/api/oauth2/**", "/api/**", "/swagger-ui/**", "/api-docs/**", "/api/mail/send").permitAll()
.requestMatchers("/api/admin").hasAuthority(Role.ROLE_ADMIN.name())
.anyRequest().authenticated()
);
//oauth2Login
http.oauth2Login(oauth2-> oauth2
.authorizationEndpoint(auth->auth.baseUri("/api/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository))
.redirectionEndpoint(redirect -> redirect.baseUri("/api/oauth2/callback/*"))
.userInfoEndpoint(user -> user.userService(customOAuth2UserService))
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler)
);
// 로그아웃 관련 설정
http.
logout(logout -> logout.logoutUrl("/api/user/auth/logout").permitAll()
.deleteCookies("JSESSIONID")
.logoutSuccessHandler((request, response, authentication) ->
response.sendRedirect(FRONTEND_BASE_URL))
);
//jwt filter 설정
http.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, customUserDetailsService), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}