Springboot oauth2 구글 로그인 구현

source

dependency

implementation("org.springframework.boot:spring-boot-starter-oauth2-client")

application.yml

spring:
    security:
        oauth2:
            client:
                registration:
                    google:
                        client-id: client-id
                        client-secret: client-secret
                        scope: profile,email

로그인 화면

<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            // 로그인 된 경우
                Logged in as: <span id="user">사용자 이름</span>
                <a href="/logout" class="btn btn-info active" role="button">Logout</a>
            // 로그인 된 경우
            // 로그인 되지 않은 경우
                <a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
            // 로그인 되지 않은 경우
        </div>
    </div>
    <br>
</div>

Security config

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {

    @Autowired
    private lateinit var customOAuth2UserService: CustomOAuth2UserService

    override fun configure(http: HttpSecurity) {
        http
            .csrf().disable()
            .headers().frameOptions().disable()

            .and()
            .authorizeRequests()
            .antMatchers(
                "/",
                "/css/**",
                "/images/**",
                "/js/**",
                "/h2-console/**"
            ).permitAll()
            .antMatchers("/api/v1/**").hasRole(Role.USER.name)
            .anyRequest().authenticated()

            .and()
            .logout()
            .logoutSuccessUrl("/")

            .and()
            .oauth2Login() // OAuth2 로그인 기능에 대한 진입점
            .userInfoEndpoint() // OAuth2 로그인 성공 이후 사용자 정보를 가져올 때의 설정을 담당
            .userService(customOAuth2UserService) // 소셜 로그인 성공 시 후속 조치를 진행할 UserService 인터페이스의 구현체 등록
    }
}

CustomOAuth2UserService

@Service
class CustomOAuth2UserService(
    private val userRepository: UserRepository,
) : OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User {
        val delegate = DefaultOAuth2UserService()
        val oAuth2User = delegate.loadUser(userRequest)

        val registrationId = userRequest.clientRegistration.registrationId;
        val userNameAttributeName =
            userRequest.clientRegistration.providerDetails
                .userInfoEndpoint.userNameAttributeName

        val attributes = OAuthAttributes.of(
            registrationId,
            userNameAttributeName,
            oAuth2User.attributes
        )

        val user = saveOrUpdate(attributes)

        return DefaultOAuth2User(
            setOf(SimpleGrantedAuthority(user.role.key)),
            attributes.attributes,
            attributes.nameAttributeKey
        )
    }

    private fun saveOrUpdate(attributes: OAuthAttributes): User {
        val user = userRepository.findByEmail(attributes.email).map {
            it.update(
                name = attributes.name,
                picture = attributes.picture
            )
            it
        }.orElse(attributes.toEntity())
        return userRepository.save(user)
    }
}