Super Kawaii Cute Cat Kaoani [인스타그램 클론 코딩][Spring boot] 13. 페이스북 소셜 로그인 구현

[인스타그램 클론 코딩][Spring boot] 13. 페이스북 소셜 로그인 구현

2024. 3. 2. 21:48
728x90
SMALL

 

⚡️ OAuth2-client 라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

 

⚡️ facebook-developer에 등록

 

⚡️ application-oauth.yml 설정

spring:
  security:
    oauth2:
      client:
        registration:
          facebook:
            clientId:
            client-secret:
            scope:
              - email
              - public_profile

 

⚡️ securityConfig 

package yerong.InstagramCloneCoding.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import yerong.InstagramCloneCoding.config.oauth.OAuth2DetailsService;

@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig{

    private final OAuth2DetailsService oAuth2DetailsService;
    @Bean
    public BCryptPasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public SecurityFilterChain filterChain (HttpSecurity http) throws Exception{
        return http
                .csrf(csrfConfig ->
                        csrfConfig.disable())
                .authorizeHttpRequests(authorizationRequest ->
                        authorizationRequest
                                .requestMatchers(
                                        AntPathRequestMatcher.antMatcher("/"),
                                        AntPathRequestMatcher.antMatcher("/user/**"),
                                        AntPathRequestMatcher.antMatcher("/image/**"),
                                        AntPathRequestMatcher.antMatcher("/subscribe/**"),
                                        AntPathRequestMatcher.antMatcher("/comment/**"),
                                        AntPathRequestMatcher.antMatcher("/api/**")

                                ).authenticated()
                                .anyRequest().permitAll()
                )
                .formLogin(formConfig ->
                        formConfig.loginPage("/auth/signin") //GET
                                .loginProcessingUrl("/auth/signin") //POST
                                .defaultSuccessUrl("/"))
                .oauth2Login(oAuth -> oAuth
                        .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig
                                .userService(oAuth2DetailsService)))
                .build();
    }
}
  • userInfoEndpoint() : oauth2로그인을 하면 최종 응답으로 회원 정보를 바로 받을 수 있음(코드 x)

 

⚡️ OAuth2DetailsService

package yerong.InstagramCloneCoding.config.oauth;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import yerong.InstagramCloneCoding.config.auth.PrincipalDetails;
import yerong.InstagramCloneCoding.domain.user.Role;
import yerong.InstagramCloneCoding.domain.user.User;
import yerong.InstagramCloneCoding.handler.exception.CustomException;
import yerong.InstagramCloneCoding.repository.user.UserRepository;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Service
@Slf4j
@RequiredArgsConstructor
public class OAuth2DetailsService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        log.info("facebook 로그인");
        OAuth2User oAuth2User = super.loadUser(userRequest);
        System.out.println(oAuth2User.getAttributes());

        Map<String, Object> userInfo = oAuth2User.getAttributes();
        String username = "facebook_" + (String) userInfo.get("id");
        String name = (String) userInfo.get("name");
        String email = (String) userInfo.get("email");
        String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());

        Optional<User> findUserOptional = userRepository.findByUsername(username);

        if(!findUserOptional.isPresent()){ //회원가입
            User user = User.builder()
                    .username(username)
                    .name(name)
                    .email(email)
                    .password(password)
                    .role(Role.USER)
                    .build();
            return new PrincipalDetails(userRepository.save(user), userInfo);
        }
        else{ //로그인
            return new PrincipalDetails(findUserOptional.get(), userInfo);
        }
    }
}
  • name, email은 페이스북에서 받아온 정보를 사용
  • username은 페이스북으로 넘어온 id값을 이용해 중복되지 않게 랜덤으로 만들어줘야함
  • password는 BCryptPasswordEncoder를 이용해 랜덤으로 만들어줘야 함

 

⚡️ PrincipalDetails

package yerong.InstagramCloneCoding.config.auth;

import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import yerong.InstagramCloneCoding.domain.user.User;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

@Getter
@Setter
public class PrincipalDetails implements UserDetails, OAuth2User {
    private User user;
    Map<String, Object> attributes;
    public PrincipalDetails(User user){
        this.user = user;
    }
    public PrincipalDetails(User user, Map<String, Object> attributes){

        this.user = user;
        this.attributes = attributes;
    }

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collector = new ArrayList<>();
        collector.add(() -> user.getRole().getKey());
        return collector;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}
  • OAuth2User를 추가해줌

 

 

 


  • JWT 방식이나 보안적으로는 약하지만, 기본적인 구현에는 성공하였당 
  • 추후에 jwt 방식으로 변환하는 방법으로 추가해보고 카카오로그인도 추가해볼 예정이다 

 

 


PREV

 

[인스타그램 클론 코딩][Spring boot] 12. AOP 처리

📌 유효성 검사 자동화 AOP : Aspect Orientied Programing - 관점 지향 프로그래밍 관점 지향 프로그래밍 + 객체 지향 프로그래밍 함께 사용할 것 ✅ 로그인 전처리 : 유효성 검사, 보안처리 username, password

nyeroni.tistory.com

 

728x90
SMALL

 

728x90
LIST

BELATED ARTICLES

more