From 9f8cbe64767b96004028e739a49d4e3c279e3c13 Mon Sep 17 00:00:00 2001 From: Baek Gi Chan Date: Sun, 28 Jul 2024 23:08:29 +0900 Subject: [PATCH] #97 - Add OAuth authentication configuration Add configuration to allow Spring Security to accept both existing DB authentication and OAuth authentication. --- .../projectboard/config/SecurityConfig.java | 95 ++++++++++++++----- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/laphayen/projectboard/config/SecurityConfig.java b/src/main/java/com/laphayen/projectboard/config/SecurityConfig.java index 142d19f..9df956f 100644 --- a/src/main/java/com/laphayen/projectboard/config/SecurityConfig.java +++ b/src/main/java/com/laphayen/projectboard/config/SecurityConfig.java @@ -1,53 +1,98 @@ package com.laphayen.projectboard.config; -import com.laphayen.projectboard.dto.UserAccountDto; import com.laphayen.projectboard.dto.security.BoardPrincipal; -import com.laphayen.projectboard.repository.UserAccountRepository; +import com.laphayen.projectboard.dto.security.KakaoOAuth2Response; +import com.laphayen.projectboard.service.UserAccountService; import org.springframework.boot.autoconfigure.security.reactive.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.SecurityFilterChain; +import java.util.UUID; + +import static org.springframework.security.config.Customizer.withDefaults; + @Configuration public class SecurityConfig { @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(auth -> auth - .requestMatchers(String.valueOf(PathRequest.toStaticResources().atCommonLocations())).permitAll() - .requestMatchers( - HttpMethod.GET, - "/", - "/articles", - "/articles/search-hashtag", - "/css/**","/scripts/**","/plugin/**","/fonts/**" - ).permitAll() - .anyRequest().authenticated() - ) - .formLogin(Customizer.withDefaults()) - .logout(logout -> logout - .logoutSuccessUrl("/") - ); - return http.build(); + public SecurityFilterChain securityFilterChain( + HttpSecurity http, + OAuth2UserService oAuth2UserService + ) throws Exception { + return http + .authorizeHttpRequests(auth -> auth + .requestMatchers(String.valueOf(PathRequest.toStaticResources().atCommonLocations())).permitAll() + .requestMatchers( + HttpMethod.GET, + "/", + "/articles", + "/articles/search-hashtag", + "/css/**","/scripts/**","/plugin/**","/fonts/**" + ).permitAll() + .anyRequest().authenticated() + ) + .formLogin(withDefaults()) + .logout(logout -> logout.logoutSuccessUrl("/")) + .oauth2Login(oAuth -> oAuth + .userInfoEndpoint(userInfo -> userInfo + .userService(oAuth2UserService) + ) + ) + .build(); } @Bean - public UserDetailsService userDetailsService(UserAccountRepository userAccountRepository) { - return username -> userAccountRepository - .findById(username) - .map(UserAccountDto::from) + public UserDetailsService userDetailsService(UserAccountService userAccountService) { + return username -> userAccountService + .searchUser(username) .map(BoardPrincipal::from) .orElseThrow(() -> new UsernameNotFoundException("유저를 찾을 수 없습니다 - username: " + username)); } + @Bean + public OAuth2UserService oAuth2UserService( + UserAccountService userAccountService, + PasswordEncoder passwordEncoder + ) { + final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService(); + + return userRequest -> { + OAuth2User oAuth2User = delegate.loadUser(userRequest); + + KakaoOAuth2Response kakaoResponse = KakaoOAuth2Response.from(oAuth2User.getAttributes()); + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + String providerId = String.valueOf(kakaoResponse.id()); + String username = registrationId + "_" + providerId; + String dummyPassword = passwordEncoder.encode("{bcrypt}" + UUID.randomUUID()); + + return userAccountService.searchUser(username) + .map(BoardPrincipal::from) + .orElseGet(() -> + BoardPrincipal.from( + userAccountService.saveUser( + username, + dummyPassword, + kakaoResponse.email(), + kakaoResponse.nickname(), + null + ) + ) + ); + }; + + } + @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder();