반응형

OAuth 로그인이란?

  • 요즘 대부분의 사이트에서 카카오, 네이버, 구글 로그인 등을 지원함
  • 사이트에서 직접 회원가입을 하지 않고 위의 소셜 서비스에 로그인을 함으로써 로그인을 할 수 있게 해줌
  • 이러한 로그인 과정을 OAuth 로그인이라고 함

구글 로그인 예제

구글 API 콘솔에서 프로젝트 생성

  1. 구글 API 콘솔 접속
  2. 새 프로젝트 생성 (프로젝트명 : OAuth-Login)
  3. 방금 생성한 프로젝트로 이동 후 OAuth 동의 화면 탭으로 이동

  1. 외부 선택 후 만들기 클릭
  2. 앱 이름(MyApp), 사용자 지원 이메일 등 필수 칸들을 채워주고 저장 후 계속 클릭
  3. 다음 버튼을 계속 눌러 생성 완료

  1. 사용자 인증 정보 탭으로 이동
  2. 사용자 인증 정보 만들기 클릭
  3. OAuth 클라이언트 ID 클릭
  4. 원하는 애플리케이션 유형 선택 후 이름은 자유롭게 설정
  5. 승인된 리디렉션 URI는 일단 로컬 프로젝트이기 때문에 'http://localhost:8080/login/oauth2/code/google'로 설정해 주었음
    • 프로젝트의 주소에 맞는 URL을 넣어주면 사용 가능하지만 /login/oauth2/code/google은 고정
  6. 생성이 완료되면 다음과 같이 클라이언트 ID와 클라이언트 보안 비밀번호를 다운받을 수 있음

라이브러리 추가

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

application.yml에 아래 정보 추가

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: 생성된 클라이언트 ID
            client-secret: 생성된 보안 비밀번호
            scope:
              - email
              - profile
  • 만약 프로젝트를 깃에 올리는 등의 경우 보안을 위해 client-id, client-secret을 application.yml에 적어주지 않고 환경변수로 등록하여 사용할 수도 있음
  • 키 값으로 아래 두개를 넣어주고 각각에 클라이언트 Id와 보안 비밀번호를 넣어주면 됨
    • SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-ID
    • SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT-SECRET

User 엔티티에 컬럼 추가

  • provider에는 "google"이 들어가게 되고, providerId에는 구글로 로그인한 유저의 고유 ID가 들어가게 됨
private String provider;
private String providerId;

PrincipalDetails 수정 사항

  • 전에는 UserDetails만 상속받았지만, OAuth 로그인을 위해 OAuth2User 상속받아야 함
    • 사이트에서 직접 회원가입한 User들은 UserDetails 타입으로 저장되고, OAuth로 가입한 User들을 OAuth2User로 저장되는데 이를 PrincipalDetails로 한번에 상속받아 사용할 수 있도록 하기 위해 상속
public class PrincipalDetails implements UserDetails, OAuth2User
  • attributes라는 Map을 만들고 생성자 추가 (메소드 오버로딩)
  • attributes는 구글 로그인을 통해서 받은 정보들을 그대로 담아 return 해주는 역할
private Map<String, Object> attributes;

public PrincipalDetails(User user, Map<String, Object> attributes) {
    this.user = user;
    this.attributes = attributes;
}
  • 이제 상속받은 OAuth2User의 메소드 Override 진행
@Override
public String getName() {
    return null;
}

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

PrincipalOauth2UserService 추가

  • Form을 통한 회원가입은 PrincipalDetailsService에서 진행되었음
  • PrincipalOauth2UserService는 DefaultOauth2UserService를 상속받아 회원가입을 진행시켜 줌
  • 만약 첫 OAuth 로그인이면 회원가입도 자동으로 시켜줘야 함
  • userRequest와 oAuth2User로 필요한 정보들을 추출하여 회원가입 진행
  • oAuth2User.getAttributes를 통해 구글에서 넘어온 oAuth2User에 담겨진 정보들을 로그로 출력해보면 아래와 같음
    • 고유 ID, 이름, 프로필 이미지, email 등을 받아옴
getAttributes : {sub=103058387739722400130, name=안창범, given_name=창범, family_name=안, picture=https://lh3.googleusercontent.com/a/AEdFTp5SiCyTaOLog9sDPN6QhWwsUj7xPbfj4HQF0fdC=s96-c, email=chb20050@gmail.com, email_verified=true, locale=ko}
  • 이 정보들을 사이트에서 사용할 User에 맞게끔 정보들을 추출해 저장
    • provider : 구글
    • providerId : 구글에서 해당 유저 고유 ID
    • loginId : provider_providerId 형식으로 저장해서 중복 방지
    • password : 필요없음
    • nickname : 구글에 저장된 사용자 이름으로 설정
@Service
@RequiredArgsConstructor
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder encoder;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        log.info("getAttributes : {}", oAuth2User.getAttributes());

        String provider = userRequest.getClientRegistration().getRegistrationId();
        String providerId = oAuth2User.getAttribute("sub");
        String loginId = provider + "_" +providerId;

        Optional<User> optionalUser = userRepository.findByLoginId(loginId);
        User user;

        if(optionalUser.isEmpty()) {
            user = User.builder()
                    .loginId(loginId)
                    .nickname(oAuth2User.getAttribute("name"))
                    .provider(provider)
                    .providerId(providerId)
                    .role(UserRole.USER)
                    .build();
            userRepository.save(user);
        } else {
            user = optionalUser.get();
        }

        return new PrincipalDetails(user, oAuth2User.getAttributes());
    }
}

SecurityConfig 수정

  • Form 로그인에 대한 설정 뿐 아닌 OAuth 로그인에 대한 설정도 추가적으로 필요
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final PrincipalOauth2UserService principalOauth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                // 인증
                .antMatchers("/security-login/info").authenticated()
                // 인가
                .antMatchers("/security-login/admin/**").hasAuthority(UserRole.ADMIN.name())
                .anyRequest().permitAll()
                .and()
                // Form Login 방식 적용
                .formLogin()
                // 로그인 할 때 사용할 파라미터들
                .usernameParameter("loginId")
                .passwordParameter("password")
                .loginPage("/security-login/login")     // 로그인 페이지 URL
                .defaultSuccessUrl("/security-login")   // 로그인 성공 시 이동할 URL
                .failureUrl("/security-login/login")    // 로그인 실패 시 이동할 URL
                .and()
                .logout()
                .logoutUrl("/security-login/logout")
                .invalidateHttpSession(true).deleteCookies("JSESSIONID")
                // OAuth 로그인
                .and()
                .oauth2Login()
                .loginPage("/security-login/login")
                .defaultSuccessUrl("/security-login")
                .userInfoEndpoint()
                .userService(principalOauth2UserService)
        ;
        http
                .exceptionHandling()
                .authenticationEntryPoint(new MyAuthenticationEntryPoint())
                .accessDeniedHandler(new MyAccessDeniedHandler());
    }
}

login.html 추가 사항

  • 아래의 a 태그를 클릭하면 아까 설정했던 URI로 이동하여 구글 로그인을 자동으로 진행
<a href="/oauth2/authorization/google">구글 로그인</a>

결과

  • 이제 로그인 페이지에 방금 추가했던 a 태그도 출력됨

  • 구글 로그인 클릭 시 아래와 같이 구글 로그인이 자동으로 진행됨
    • 여기서 "MyApp"은 내가 설정한 앱 이름

  • 첫 구글 로그인 시 아래와 같이 DB에 정보를 자동으로 넣어줘서 회원가입 진행
    • 4번 유저는 직접 회원가입한 유저, 5번 유저는 구글 로그인을 통해 가입한 유저

  • 인증이 필요한 페이지에도 접근됨을 확인 => 로그인 성공

반응형

↓ 클릭시 이동

복사했습니다!