Spring Boot Security 소셜 로그인 [카카오, 구글]

🌿 Security 소셜 로그인

spring_security

1. 소셜 애플리케이션 등록

참고

먼저 사용하려는 소셜 플랫폼(예: Google, Facebook, GitHub 등)의 개발자 포털에 들어가서 애플리케이션을 등록. </br> 등록 과정에서는 클라이언트 ID 및 시크릿 키와 같은 인증 정보를 발급받는다.

나의 경우 (Google, kakao)

2. 의존성 추가

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

3. application-oauth.properties 파일 생성 및 기존 properties에 내용 추가

application.properties 파일에 아래 내용 추가

# spring security OAuth
spring.profiles.include=oauth

application-oauth.properties엔 Goole, kakao 관련 값들을 넣어줌.

# Google
spring.security.oauth2.client.registration.google.client-id: 구글 클라이언트 ID
spring.security.oauth2.client.registration.google.client-secret: 구글 클라이언트 시크릿 키
spring.security.oauth2.client.registration.google.scope: 요청할 권한 범위(프로필 및 이메일)
spring.security.oauth2.client.registration.google.redirect-uri: 사용자 인증 후 리디렉션될 URI

# kakao
spring.security.oauth2.client.registration.kakao.client-id: 카카오 클라이언트 ID
spring.security.oauth2.client.registration.kakao.client-secret: 카카오 클라이언트 시크릿 키
spring.security.oauth2.client.registration.kakao.scope: 요청할 권한 범위
spring.security.oauth2.client.registration.kakao.redirect-uri: 사용자 인증 후 리디렉션될 URI
spring.security.oauth2.client.provider.kakao.authorization-uri: 카카오 OAuth2 인증 엔드포인트 URI
spring.security.oauth2.client.provider.kakao.token-uri: 카카오 OAuth2 토큰 엔드포인트 URI
spring.security.oauth2.client.provider.kakao.user-info-uri: 사용자 정보 엔드포인트 URI
spring.security.oauth2.client.provider.kakao.user-name-attribute: 사용자 이름 속성명

4. SecurityConfig 파일에 oauth2 관련 설정

// 소셜 로그인 
.oauth2Login((oauth2) -> oauth2.loginPage("/login"));

5. 로그인 페이지 소셜 로그인 경로 설정

login.html

<div class="kakao-login">
  <a th:href="@{/oauth2/authorization/kakao}">
    <img th:src="@{/images/img_kakao.png}" alt="kakao">
    <br/>
    KAKAO 
  </a>
</div>
			        
<div class="google-login">
  <a th:href="@{/oauth2/authorization/google}">
    <img th:src="@{/images/img_google.png}" alt="google">
    <br/>
    Google 
  </a>
</div>

6. Service

소셜 로그인을 통해 사용자가 처음으로 로그인하는 경우를 처리하는 메서드 생성

@Transactional
public CustBas whenSocialLogin(String providerTypeCode, String custId, String custNm, String custEmail) {
	Optional<CustBas> opCustBas = findByCustId(custId);
	
	// 존재하는 고객인 경우, 해당 고객 정보 반환
	if (opCustBas.isPresent()) { return opCustBas.get(); }
	
	// 새로운 고객 등록을 위해 고객 정보 설정
	CustBasDTO request = new CustBasDTO();
	request.setCustId(custId);
	// 소셜 로그인를 통한 가입시 비번 X
	request.setCustPassword("");
	request.setCustNm(custNm);
	request.setCustEmail(custEmail);
	request.setCustNo("Unknown");
    
	String uniqueIdfyNo = UUID.randomUUID().toString();
	request.setCustIdfyNo(uniqueIdfyNo);
       
	return save(request);
}

7. CustomOAuth2UserService 생성

@Service
@Transactional
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
	
	private static final Logger logger = LoggerFactory.getLogger(CustService.class);
	
	private final CustService custService;
	
	 @Override
	 @Transactional
	 public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
		 OAuth2User oAuth2User = super.loadUser(userRequest);
		 Map<String, Object> attributes = oAuth2User.getAttributes();
		 
		 logger.debug("소셜로그인 service");
		 logger.debug("Attributes: " + attributes.toString());
		 
		 // kakao
		 Map attributesProperties = (Map) attributes.get("properties");
		 Map attributesAccount = (Map) attributes.get("kakao_account");
		 
		 String custNm = "Unknown";
		 String custEmail = "Unknown";
		 
		 if(attributesProperties != null) {
			 custNm = (String) attributesProperties.get("nickname");
		 } else if(attributes != null) {
			 custNm = (String) attributes.get("name");
		 }
		 
		 if(attributesAccount != null) {
			 custEmail = (String) attributesAccount.get("email");
		 } else if(attributes != null) {
			 custEmail = (String) attributes.get("email");
		 }

     		 // OAuth2를 통해 인증된 사용자의 고유 식별자
		 String oauthId = oAuth2User.getName();

		 // OAuth2 클라이언트의 등록 ID
		 String providerTypeCode = userRequest.getClientRegistration().getRegistrationId().toUpperCase();

     		 //  소셜 로그인을 통해 생성된 ID
		 String custId = providerTypeCode + "_%s".formatted(oauthId);

		 // whenSocialLogin 메서드를 호출
		 CustBas custBas = custService.whenSocialLogin(providerTypeCode, custId, custNm, custEmail);
		 
		 return new CustomOAuth2User(custBas.getCustId(), custBas.getPassword(), custBas.getAuthorities());
	 }
}

// User 클래스를 확장하고 OAuth2User 인터페이스를 구현
class CustomOAuth2User extends User implements OAuth2User {

    // CustomOAuth2User의 생성자로, 사용자 이름, 비밀번호 및 권한을 받아 초기화
    public CustomOAuth2User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

    // 사용자의 추가 속성을 다루지 않음
    @Override
    public Map<String, Object> getAttributes() {
        return null;
    }

    // 사용자 이름 반환
    @Override
    public String getName() {
        return getUsername();
    }
}
  • @RequiredArgsConstructor: 필드를 기반으로 생성자를 자동으로 생성
  • loadUser: 스프링 시큐리티에서 제공하는 메서드로, OAuth2 로그인 시 사용자 정보를 로드함.
  • userRequest: OAuth2 로그인 요청에 대한 정보.
  • oAuth2User: 부모 클래스인 DefaultOAuth2UserService를 통해 사용자 정보를 가져옴.
  • attributesProperties, attributesAccount: 소셜 플랫폼에 따라 다른 속성을 가진 사용자 정보를 담고 있음.

Leave a comment