728x90

개요

이번에는 구글 OAuth2 로그인에 대해서 작성해볼려고 한다. 

 

Spring Security + OAuth2 + Session 를 사용한 로그인, 로그아웃 구현 - Kakao, Naver :: 미정 (tistory.com)

 

Spring Security + OAuth2 + Session 를 사용한 로그인, 로그아웃 구현 - Kakao, Naver

개요시큐리티를 사용해 OAuth2 로그인을 진행하고자 한다. 나는 그중 카카오와 네이버에 대해 해보겠다.앞에서 진행한 코드를 그대로 사용해서 이어서 진행한다. Spring Security + Session 를 사용한

eesko.tistory.com

위 코드를 그대로 사용할 예정이다.

 

구글 소셜 로그인 신청

일단 구글 관련해서는 다루지 않을 것이고 이 부분은 다른 블로그를 찾아보면 된다.

 

 

나의 환경

Window 11

intelliJ

java 21 

spring Boot 3.3.0

spring Security 6

oauth2

 

 

프로젝트 파일 구조

 

의존성 설치

  • Build.gradle
	// OAuth2 Client
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  • Build.gradle 에 security와 OAuth2 의존성을 추가해준다.

 

  • Application.yml
spring:
  application:
    name: [projectname]

  datasource:
    url: jdbc:mysql://localhost:3306/[db]
    username: [usernmae]
    password: [password]
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: false
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQLDialect

  security:
    oauth2:
      client:
        registration:
          google:
            client-id: [client-id]
            client-secret: [client-secre]
            client-name: google
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/google
            scope:
              - profile
              - email
server:
  port: 8080
  • application.yml 파일에 OAuth2 설정을 해준다. 
  • 구글같은 경우 provider 부분은 스프링에서 알아서 제공해줘서 작성안해줘도 된다.

 


1. GoogleResponse

getAttributes :
{
	resultcode=00,
    	message=success,
      	id=12345678,
        email=엉이@naver.com,
      	name=엉이
   	}
 }
 
 provider : google

구글 같은 경우 응답 방식인 response 가 위와 같이 나온다. 저거에 맞게 커스텀하면 된다.

public class GoogleResponse implements OAuth2Response{

    private final Map<String, Object> attribute;

    public GoogleResponse(Map<String, Object> attribute) {

        this.attribute = attribute;
    }

    @Override
    public String getProvider() {

        return "google";
    }

    @Override
    public String getProviderId() {

        return attribute.get("sub").toString();
    }

    @Override
    public String getEmail() {

        return attribute.get("email").toString();
    }

    @Override
    public String getName() {

        return attribute.get("name").toString();
    }


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

각 소셜 플랫폼마다 웅답 방식이 다르기 때문에 인터페이스를 구현했고 구글 로그인을 위해 내가 만든 인터페이스를 상속하는 방식으로 진행했다. 이 부분은 이전 게시글을 보면 된다.

 

 

 

2. OAuth2UserService 구현

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

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

        OAuth2User oAuth2User = super.loadUser(userRequest);
        System.out.println("OAuth2User attributes: " + oAuth2User.getAttributes());
        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        OAuth2Response oAuth2Response = null;

        switch (registrationId) {
            case "naver":
                log.info("naver 로그인");
                oAuth2Response = new NaverResponse(oAuth2User.getAttributes());
                break;

            case "kakao":
                log.info("kakao 로그인");
                oAuth2Response = new KakaoResponse(oAuth2User.getAttributes());
                break;

            case "google":
                log.info("google 로그인");
                oAuth2Response = new GoogleResponse(oAuth2User.getAttributes());
                break;

            default:
                log.error("로그인 실패: 지원하지 않는 로그인 제공자입니다. 등록 ID: {}", registrationId);
                throw new IllegalArgumentException("지원하지 않는 로그인 제공자입니다.");
        }


        String provider = oAuth2Response.getProvider();
        String providerId = oAuth2Response.getProviderId();

        String name = provider + " " + providerId; // 이렇게 해서 해당 유저가 이미 디비에 있는지 없는지 확인
        Optional<User> userOptional = userRepository.findByUsername(name);

        // "USER" 라는 역할을 OAuth2 로그인 사람에게 다 부여
        String roleName = "ROLE_USER";
        Optional<Role> roleOptional = roleRepository.findByName(roleName); // 디비에서 찾아오는데,
        Role role;
        if (roleOptional.isEmpty()) { // "USER" 디비에 없다면
            role = new Role(roleName); // 새로운 역할 등록
            role = roleRepository.save(role);
        } else {
            role = roleOptional.get(); // 그게 아니라면 역할 꺼내오기
        }

        String password = String.valueOf(UUID.randomUUID());

        User user;
        // OAuth2 로그인을 한 적 없는 사람
        if (userOptional.isEmpty()) {
            user = User.builder()
                    .name(oAuth2Response.getEmail())
                    .username(name)
                    .roles(Set.of(role))
                    .providerId(oAuth2Response.getProviderId())
                    .provider(oAuth2Response.getProvider())
                    .password(password)
                    .phoneNumber("default")
                    .birthdate(LocalDate.from(LocalDateTime.now()))
                    .gender("default")
                    .registrationDate(LocalDateTime.now())
                    .usernick(oAuth2Response.getName())
                    .build();
            userRepository.save(user);
        } else { // 이미 OAuth2 로그인을 한 적이 있는 사람
            user = userOptional.get();
            boolean updated = false;

            if (!user.getRoles().contains(role)) {
                user.getRoles().add(role);
                updated = true;
            }

            // 닉네임은 첫 로그인 이후 마이페이지에서만 변경 가능
            if (!user.getUsernick().equals(oAuth2Response.getName()) && user.getUsernick() == null) {
                user.setUsernick(oAuth2Response.getName());
                updated = true;
            }

            if (updated) {
                userRepository.save(user);
            }
        }

        System.out.println("User saved: " + user);

        // 특정 사이트의 응답 값과 역할을 받는 CustomOAuth2User 클래스
        // 로그인 한적 없는 사람은 "USER" 역할, 기존에 한 적있다면 그 사람이 현재 갖고 있는 역할을 CustomOAuth2User 클래스로 반환
        return new CustomOAuth2User(oAuth2Response, roleName);
    }
}
  • Spring Security의 OAuth2 인증을 처리하는 커스텀 서비스이다.
  • 주로 네이버, 카카오, 구글 등의 OAuth2 제공자에서 사용자 인증 정보를 받아와 데이터베이스에 저장하거나 업데이트하는 역할을 한다.
  • loadUser 메서드는 네이버나 카카오 등 소셜플랫폼의 사용자 인증 정보를 받아오는 메서드이다.
  • 위 클래스는 외부 사이트로부터 사용자 정보를 받아오고 그 값을 디비에 저장하는 클래스이다.

 

 

3. 로그인 폼 및 로그인 한 회원의 정보를 보여주기

  • loginform.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr/>
<!-- 시큐리티는 x-www-form-url-encoded 타입만 인식 -->
<form action="/login" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <button>로그인</button>
</form>
<a href="/oauth2/authorization/kakao">카카오 로그인</a>
<a href="/oauth2/authorization/naver">네이버 로그인</a>
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/api/users/signup">회원가입을 아직 하지 않으셨나요?</a>
</body>
</html>

 

이렇게 하면 기본 Oauth2 세션 로그인은 성공할 것이다. 시큐리티 관련 설정은 이전 게시글에서 다뤘기 때문에 여기서 따로 적지는 않을 것이다.

다음 포스터에서는 모든 소셜로그인에서  jwt를 사용해서 구현해볼 것이다.

 

728x90

+ Recent posts