[Spring Security] 카카오 OAuth 로그인 구현하기
카카오 OAuth 간편 인증을 구현하는 과정을 차례대로 기록해 보겠습니다.
OAuth2.0 기본 흐름
OAuth란 "다른 서비스에 비밀번호를 알려주지 않고 내 정보를 제공하거나 인증을 맡기는 방법"이다. 이번 예제인 카카오 로그인 외에도 일반적인 OAuth2.0의 기본 흐름은 아래와 같다(네이버, 구글 등).
- 사용자가 "카카오 로그인" 버튼 클릭 및 인증 진행
- 카카오가 사용자에게 "동의 요청" 확인(이 사이트에 생년월일 정보를 제공해도 돼?)
- 사용자 "동의"클릭시, 카카오는 사이트에게 인가 코드(Authorization Code) 전달
- 사이트는 Authorization Code를 들고 카카오에 Access Token 요청
- 카카오는 사이트에게 Access Token 전달
- 사이트는 Access Token을 들고 카카오에 "사용자 정보 요청" API 호출
- 카카오가 사용자 정보 (이메일, 이름 등)를 사이트에 반환
- 사이트는 받은 정보로 회원가입/로그인 처리
카카오 개발자 센터 가입 및 내 애플리케이션 등록
카카오 인증과 관련된 설정들은 카카오 개발자 센터에서 관리할 수 있다.
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
동의 항목 설정
어떤 쇼핑몰 사이트를 가입하려고 했을 때, 여기서 선택한 항목들이 홈페이지 첫 로그인 시 확인창으로 나타나게 된다. 만약 위 사진처럼 닉네임과 프로필 사진만 허용되어 있다면, 개인정보 동의 시 쇼핑몰은 설정해놨던 닉네임과 프로필 사진 정보만 kakao로부터 받아 회원가입 처리를 진행한다는 것을 의미한다. 아래에 설명할 application.yml 설정 내 scope 옵션에는 동의 설정한 항목 이외에 다른 ID값을 적혀있으면 에러가 발생한다.
Kakao 로그인 호출 화면 만들기
클릭하면 "/oauth2/authorization/kakao" 를 호출하는 버튼을 간단히 만들었다. 이 URI은 Spring Security가 자동으로 만들어주는 로그인 트리거용 주소이다. 해당 주소를 호출하면 Spring Security가 자동으로 yml에 설정한 authorization-uri (https://kauth.kakao.com/oauth/authorize)로 redirect 시켜준다.
<body>
<div class="login-container">
<a href="/oauth2/authorization/kakao">
<img src="/kakao_login_medium_narrow.png" alt="카카오 로그인">
</a>
</div>
</body>
버튼으로 사용한 이미지는 여기에서 제공받을 수 있다.
Springboot 프로젝트 설정
Kakao 간편 인증 처리를 위한 application.yml 설정은 아래와 같다.
spring:
security:
oauth2:
client:
registration:
kakao:
client-id: [카카오 개발자 센터에서 발급받은 앱 키 (REST API 키)]
client-secret: [비워도 됨]
redirect-uri: "{baseUrl}/login/oauth2/code/kakao"
authorization-grant-type: authorization_code
scope: profile_nickname
client-name: Kakao
provider:
kakao:
authorization-uri: https://kauth.kakao.com/oauth/authorize
token-uri: https://kauth.kakao.com/oauth/token
user-info-uri: https://kapi.kakao.com/v2/user/me
user-name-attribute: id
옵션 | 내용 |
redirect-uri | 사용자가 인증을 완료하고 나서 카카오로부터 Authorization Code를 수신받을 주소이다. 기본적으로 Spring Security가 OAuth 처리를 위해 인가 코드를 수신받는 URI 형식은 "/login/oauth2/code/{registrationId}"으로 약속돼있다! 여기서 registrationId는 "kakao"이기 때문에 위와 같이 설정. redirect-uri를 커스터마이징해서 다른 경로로 바꾸면 Spring Security는 그 경로를 인식/처리하지 못한다. |
authorization-uri | 카카오에 인가 요청을 보낼 실제 주소이다. 회신될 Authorization Code는 인증 과정이 성공했다는 "확인용 1회용 코드"라고 생각하자. Access Token을 브라우저에 직접 노출시키지 않기 위해 단계를 분리시켜놓은 것. |
token-uri | 인증이 완료된 후 "사용자 정보에 실제 요청/접근할 수 있는 열쇠"인 Access Token을 요청할 주소이다. 쇼핑몰 입장에서는 이 Access Token이 사용자가 인증됐다는 증거가 된다. 로그인할 때 토큰만 먼저 발급하는 이유는 사용자 정보의 노출을 줄이기 위함이며, 사이트가 정말 필요한 데이터만 API를 통해 명시적으로 요청하게끔 강제하기 위해서이다. |
user-info-uri | 사용자 정보를 요청할 주소이다. Spring Security는 access token을 발급받은 직후에, 사용자 정보를 가져오기 위해 자동으로 user-info-uri에 HTTP 요청을 보낸다. 즉, 쇼핑몰은 Access Token을 들고 카카오에 사용자 정보를 요청한다. |
사이트 도메인 및 Redirect URI 설정하기
카카오 개발자 센터에 내 애플리케이션의 도메인과 인가 코드(Authorization Code)를 받을 Redirect URI를 등록해야 한다. 특히 Redirect URI는 yml에 설정했었던 redirect-uri 옵션과 반드시 동일하게 작성해야만 한다.
Spring Security(SecurityFilterChain) 추가 설정하기
@Configuration
public class SecurityConfig {
@Autowired
CustomOAuth2UserService customOAuth2UserService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
HttpSecurity httpSecurity = http
.authorizeHttpRequests(auth -> auth
// 로그인 페이지 접근 허용(이외 경로들은 접근시 인증 필요)
.requestMatchers("/", "/login", "/css/**", "/js/**", "/*.png").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/") // 로그인 페이지 설정
.defaultSuccessUrl("/home", true) //로그인 성공시 보낼 주소 작성
.failureUrl("/")
// 사용자 정보 요청이 완료되면, customOAuth2UserService를 호출
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
);
return http.build();
}
}
정적 리소스 및 /, /login 등의 로그인 관련 페이지들은 인증 없이 접근을 허용하도록 Security 설정을 수정해야 한다. 특정 주소 접근에 인증이 필요한데, 로그인이 안 되어 있는 경우에 redirect할 페이지는 loginPage()에 지정하면 된다. user-info-uri로 요청했던 사용자 정보를 수신하면 userInfoEndpoint()에 이 정보를 처리하기 위한 메서드를 설정할 수 있다.
OAuth2 인증 후 '사용자 정보'요청 처리
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 카카오로부터 사용자 정보 받아오기
OAuth2User oauth2User = new DefaultOAuth2UserService().loadUser(userRequest);
System.out.println("카카오 사용자 정보: " + oauth2User.getAttributes());
// OAuth2User 객체를 리턴해야 세션에 인증정보 저장됨
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")), // 권한
oauth2User.getAttributes(), // 사용자 정보 전체
"id" // 사용자 식별자 키 (카카오 기본값: "id")
);
}
}
OAuth2UserService는 '사용자 정보'요청 이후, 그 응답을 처리하기 위한 인터페이스이다. OAuth2User 객체를 만들어주는 역할을 담당한다.