반응형
- 카카오 디벨로퍼 사이트 접속
- 내 애플리케이션에서 앱 이름, 사업자 정보 등 기입 사업자 정보는 그냥 앱이름이랑 같이했음
- 이렇게 4가지가 있는데 REST API 키를 복사한다 이게 서버에서 사용할 client-id값이 된다.
- 밑에 플랫폼에 들어가서
- 이렇게 작성
- 이제 여기에 들어가주면 됨
- 동의 항목 설정
- 이메일도 필요하니
- 이제 활성화 설정을 on을 해주고
- 리다이렉트 url을 이렇게 작성하는데 밑 처럼 작성을 한다.
- Spring OAuth2 Client 라이브러리를 사용하면 기본적으로 Redirect URI을 /local/oauth2/code/{registrationId}로 Redirect 해준다. registrationId도 밑에서 알아보자.
- http://localhost:8080/login/oauth2/code/kakao이렇게 작성해도 됨
- 현재 작성된 거는 내가 연결한 와이파이 IP임
- 위 보안 페이지로 넘어가서 밑 client secret을 생성해주자
- 이제 이게 client-secret값이 된다.
이제 설정은 여기가 끝임.
코드를 작성해보자
- gradle 설정
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
LoginController.java
package com.sal.saltbackend.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/login")
public class LoginController {
@GetMapping
public String login(){
return "login";
}
}
login.html
- 카카오 로그인을 눌렀을 때 href에 있는 걸 요청하게끔 하는 거
- Spring OAuth2 Client 라이브러리에서 기본으로 설정된 경로이며 이 경로 또한 커스텀 가능
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/oauth2/authorization/kakao">카카오로그인</a>
</body>
</html>
application.properties에 밑을 넣어준다
- 밑에 클라이언트 ID, 시크릿 값은 아까 디펠로퍼 사이트에서 생성한 값을 넣어주자 즉 client-id와 client-secret 값이다
- 클라이언트 ID → 앱키의 REST API 키
- 클라이언트 시크릿 값
- redirect-url은 카카오 디벨로퍼 사이트에서 설장한 값 세팅
- client-id와 client-secret 값은 절대 외부에 노출 시키면 안된다
위 코드 설명
이 설정 파일은 Spring Security OAuth2 클라이언트를 사용하여 카카오 로그인 기능을 구현하는 데 필요한 설정
1. spring.security.oauth2.client.registration.kakao.client-id
- 카카오 개발자 콘솔에서 발급받은 애플리케이션의 고유 클라이언트 ID
- 역할: OAuth2 인증 과정에서 클라이언트를 식별하는 데 사용. 카카오 서버에 인증 요청을 보낼 때 이 값을 사용하여 클라이언트를 식별
2. spring.security.oauth2.client.registration.kakao.client-secret
- 카카오 개발자 콘솔에서 발급받은 클라이언트 비밀키
- 역할: 클라이언트의 비밀 인증 정보로, OAuth2 인증 과정에서 클라이언트의 신뢰성을 증명하는 데 사용. 클라이언트 ID와 함께 사용되어 클라이언트를 안전하게 인증
3. spring.security.oauth2.client.registration.kakao.client-authentication-method
- 클라이언트 인증 방식
- 역할: 클라이언트를 인증하는 방법을 지정. 여기서는 client_secret_post 방식으로 설정되어 있는데, 이는 클라이언트 ID와 클라이언트 비밀키를 POST 요청의 본문에 포함시켜 서버에 전송하는 방식
- 좀 더 설명 OAuth2 클라이언트가 인증 서버에 요청을 보낼 때 클라이언트를 인증하는 방법
- client_secret_basic: 이 방식은 클라이언트 ID와 클라이언트 비밀을 HTTP 기본 인증(Basic Authentication) 헤더에 포함시켜 전송. 이 방식은 클라이언트 인증 정보를 HTTP 요청의 헤더에 Base64 인코딩된 형태로 포함시긴다.
- client_secret_post: 이 방식은 클라이언트 ID와 클라이언트 비밀을 POST 요청의 본문에 포함시켜 전송. 클라이언트 인증 정보는 일반적인 폼 파라미터(client_id, client_secret)로 전송
- client_secret_jwt: 클라이언트가 자신을 인증하기 위해 클라이언트 비밀과 함께 JWT(JSON Web Token)를 사용. 이 방식은 클라이언트와 인증 서버 사이에 비밀 공유가 필요.
- private_key_jwt: 클라이언트가 자신을 인증하기 위해 공개 키를 사용한 JWT를 사용 이 방식은 클라이언트의 비밀 정보가 아닌 공개 키를 기반으로 인증이 이루어진다.
- 사용 목적 → OAuth2 프로토콜에서 클라이언트 인증은 중요한 역할을 합니다. 인증 서버는 클라이언트를 확인하고, 올바른 클라이언트에게만 액세스 토큰을 발급해야 하기 때문에 클라이언트 인증 방법을 지정
4. spring.security.oauth2.client.registration.kakao.redirect-uri
- OAuth2 인증 과정에서 사용자가 인증을 완료한 후 리디렉션될 URI
- 역할: 카카오 인증 서버에서 인증을 완료한 후, 이 URI로 사용자를 리디렉션. 이 URI는 카카오 개발자 콘솔에도 등록되어 있어야 하며, 일치하지 않으면 인증 오류가 발생
5. spring.security.oauth2.client.registration.kakao.authorization-grant-type
- OAuth2 인증 과정에서 사용할 권한 부여 방식(Grant Type)
- 역할: 여기서는 authorization_code가 사용되고 있는데, 이는 사용자에게 카카오 로그인 화면을 제공하고, 사용자가 인증을 완료하면 인증 코드를 클라이언트에게 전달하는 방식. 클라이언트는 이 인증 코드를 사용하여 액세스 토큰을 받음.
- OAuth2 인증 과정에서 사용되는 권한 부여 방식(Grant Type)을 지정하는 설정
- authorization_code: 이 방식은 가장 많이 사용되는 OAuth2 권한 부여 방식. 사용자가 인증 서버(예: 카카오)에서 로그인을 완료한 후, 인증 서버는 클라이언트에게 인증 코드를 발급한다. 클라이언트는 이 인증 코드를 사용하여 액세스 토큰을 요청. 이 방식은 클라이언트가 사용자로부터 직접 인증 자격 증명을 수집하지 않으므로 보안이 우수
- implicit: 이 방식은 클라이언트가 서버 자원이 아닌 클라이언트 측(예: 자바스크립트 애플리케이션)에서 액세스 토큰을 직접 받을 수 있게 하는 방식. 인증 코드 없이 직접 액세스 토큰을 받기 때문에 보안성이 낮아 현재는 잘 사용되지 않으며, 특히 민감한 정보가 포함된 API에 대해 권장되지 않는다.
- password: 이 방식은 리소스 소유자 비밀번호 인증 방식. 사용자가 자신의 사용자 이름과 비밀번호를 클라이언트에 직접 제공하여, 클라이언트가 이를 이용해 인증 서버에서 액세스 토큰을 요청을 한다. 이 방식은 보안이 상대적으로 낮고, 사용자의 자격 증명이 클라이언트에 노출되기 때문에 일반적으로 권장되지 않는다.
- client_credentials: 이 방식은 클라이언트 자신이 액세스 토큰을 요청하는 방식. 이 경우, 사용자가 아닌 클라이언트가 리소스 서버에 접근할 수 있도록 액세스 토큰을 요청하는데, 보통 서버 간 통신에서 사용. 이 방식에서는 사용자 개입이 없다.
- refresh_token: 이 방식은 기존에 발급된 액세스 토큰이 만료되었을 때, 새로운 액세스 토큰을 발급받기 위해 사용. 클라이언트는 기존의 리프레시 토큰을 사용하여 새로운 액세스 토큰을 요청할 수 있다.
- 사용 목적 → OAuth2 인증 프로세스는 다양한 시나리오에 맞게 설계되어 있으며, 각 시나리오에 따라 적합한 권한 부여 방식을 선택
6. spring.security.oauth2.client.registration.kakao.client-name
- 이 클라이언트 등록의 이름
- 역할: 여러 OAuth2 클라이언트를 구성할 때 각각의 클라이언트를 구분하는 데 사용되는 이름. 이 이름은 기본적으로 UI와 같은 곳에서 사용자가 볼 수 있는 이름으로도 표시될 수 있음.
7. spring.security.oauth2.client.registration.kakao.scope
- OAuth2 인증 과정에서 요청할 권한 범위(scope)
- 역할: 카카오 API에서 사용자로부터 동의를 받을 때 어떤 정보에 접근할 것인지를 지정 여기서는 profile_nickname, account_email, profile_image의 세 가지 정보에 대한 접근 권한을 요청
8. spring.security.oauth2.client.provider.kakao.authorization-uri
- 설명: 카카오의 OAuth2 인증 URL
- 역할: 사용자를 카카오 로그인 화면으로 리디렉션할 때 사용되는 URL. 이 URL은 인증 요청을 시작하는 데 사용
9. spring.security.oauth2.client.provider.kakao.token-uri
- 설명: 카카오의 OAuth2 토큰 발급 URL
- 역할: 클라이언트가 인증 코드를 가지고 액세스 토큰을 요청하는 데 사용하는 URL입니다. 인증 코드가 유효하면 이 URL을 통해 액세스 토큰을 받을 수 있다.
10. spring.security.oauth2.client.provider.kakao.user-info-uri
- 설명: 카카오의 사용자 정보 요청 URL
- 역할: 액세스 토큰을 사용하여 카카오 서버에서 사용자의 프로필 정보를 가져올 때 사용되는 URL
11. spring.security.oauth2.client.provider.kakao.user-name-attribute
- 카카오에서 사용자 정보를 가져올 때 사용자의 식별자(ID)로 사용할 속성
- 역할: OAuth2User 객체의 식별자로 사용할 속성을 지정 여기서는 카카오에서 제공하는 id 속성을 사용하여 사용자 식별자로 설정
Oauth 2.0 방식에 대해서 자세히 설명
- 백엔드 연결 설명 Oauth2 설명
- 큰 틀
1. 카카오 로그인 요청, 인가 코드 받기 요청, 인증 및 동의 요청, 로그인 및 동의
- 프론트엔드에서 /oauth2/autorization/kakao 여기로 요청
- /oauth2/autorization/kakao 왜 여기로 요청해야되냐 → Spring Security에서 자체적으로 설정 해놓음
그러면 사용자가 버튼을 누르면 카카오 로그인 화면을 띄워주는 놈이 누구냐 라는 것인데
- OAuth2LoginAuthenticationFilter
- 요청을 가로채기: 사용자가 /oauth2/authorization/kakao URL에 접근하면, OAuth2AuthorizationCodeAuthenticationProvider 들어오는 요청을 가로채고 application.properties 파일에 설정된 카카오 인증 서버 → https://kauth.kakao.com/oauth/authorize 로 리디렉션
- OAuth2AuthorizationRequestResolver
- OAuth2AuthorizationRequest 생성: 필터는 OAuth2AuthorizationRequestResolver를 사용하여 요청을 분석하고, OAuth2 인증 요청을 생성
- 인증 서버로 리디렉션: 생성된 OAuth2AuthorizationRequest를 바탕으로 사용자를 카카오 인증 서버로 리디렉션.
- 밑 주소가 카카오 서버가 프론트한테 주는 거임(Security)
- https://accounts.kakao.com/login/?continue= ~~ 뭐시기
- 이런걸 화면에 보여짐 이게 카카오톡 로그인 페이지
- 로그인을 하고 이제 동의 버튼을 누르기 전 밑 도메인
- https://kauth.kakao.com/oauth/authorize?scope=profile_nickname ~~ 뭐시기
- 위 url의 좀 더 설명
- scope: profile_nickname 및 account_email에 대한 접근 권한을 요청
- response_type: code로 설정하여 인가 코드를 요청
- state: CSRF 공격을 방지하기 위한 상태 값
- redirect_uri: 카카오가 인증 후 인가 코드를 전송할 애플리케이션의 리디렉션 URI
- through_account: 사용자가 로그인 페이지를 거치도록 설정
- client_id: 카카오 API 클라이언트 ID
- 사용자는 카카오 로그인 페이지에서 인증을 완료 후 카카오는 설정된 리디렉션 URI(http://172.20.10.12:8080/login/oauth2/code/kakao로 인가 코드 전송하고OAuth2LoginAuthenticationFilter가 인가 코드를 추출
이 모든 과정은 Spring Security가 자동으로 처리 → 코드가 없는데 다 해줌
2. 인가 코드 발급, 인가 코드로 토큰 발급 요청
- OAuth2AuthorizationCodeAuthenticationProvider
- 사용자가 로그인 및 동의까지 완료되면 추출된 인가 코드를 사용하여 카카오의 토큰 엔드포인트 (https://kauth.kakao.com/oauth/token)로 액세스 토큰을 요청.
- application.properties → spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token
- 이것도 Security가 자동으로 해줌
- 카카오에서 준 액세스 토큰 받는 코드(onAuthenticationSuccess 함수 안에 있음 지금은 주석)
//이 부분이 카카오에서 받은 토큰을 가져오는 코드임. // OAuth2AuthenticationToken을 가져옴 OAuth2AuthenticationToken oauth2AuthenticationToken = (OAuth2AuthenticationToken) authentication; // OAuth2AuthorizedClient를 사용하여 액세스 토큰을 가져옴 OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient( oauth2AuthenticationToken.getAuthorizedClientRegistrationId(), oauth2AuthenticationToken.getName());
3. 토큰으로 사용자 정보 가져오기 요청, 요청 검증 및 처리, 제공받은 사용자 정보로 서비스 회원 여부 확인
- 그러면 토큰은 OAuth2AuthenticationToken(Security)가 자동으로 저장해주고 액세스 토큰을 받음 OAuth2UserService가 사용자 정보를 요청함.
- 이 과정에서 DefaultOAuth2UserService의 loadUser 메서드가 호출되어 OAuth2UserRequest을통해 액세스 토큰과https://kapi.kakao.com/v2/user/me 엔드포인트로 요청을 보냄.
- application.properties → spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
그러면 이제 이 코드를 통해 oAuth2User가 등록됨
코드 설명
- loadUser 메서드: OAuth2 인증이 성공한 후 호출되는 메서드로, 사용자 정보를 가져오는 역할. 이 메서드는 기본적으로 DefaultOAuth2UserService의 loadUser 메서드를 오버라이드
- OAuth2UserRequest: 이 객체는 OAuth2 인증 요청에 대한 모든 정보를 포함. 예를 들어, 클라이언트의 등록 정보, 액세스 토큰, 공급자 정보 등이 포함
- DefaultOAuth2UserService delegate: DefaultOAuth2UserService 인스턴스를 생성하여, 실제로 사용자 정보를 가져오는 역할을 위임
- oAuth2User: DefaultOAuth2UserService에서 반환된 사용자 정보를 담고 있는 객체
- registrationId: 현재 로그인 요청을 처리하는 OAuth2 공급자의 식별. 예를 들어, 카카오와 같은 공급자의 경우 registrationId는 "kakao" 된다
- userNameAttributeName: OAuth2 공급자가 사용자 정보를 반환할 때, 그 중 어떤 속성을 사용자 식별자로 사용할지 결정합니다. 일반적으로 이 값은 id 또는 sub와 같은 값
- attributes: OAuth2 공급자로부터 가져온 사용자 정보가 포함된 Map 객체입니다. 이 Map에는 사용자의 이름, 이메일, 프로필 이미지 등의 정보가 포함
- DefaultOAuth2User: 이 객체는 OAuth2 사용자 정보를 나타내는 객체로, 사용자 식별자와 권한 정보 등을 포함
- SimpleGrantedAuthority("ROLE_USER"): 모든 사용자에게 기본적으로 ROLE_USER 권한을 부여. 이는 사용자의 권한을 설정하는 부분
- attributes: 위에서 가져온 사용자 정보 맵이 여기에 포함
- userNameAttributeName: 사용자 식별자로 사용할 속성을 지정 이 속성은 DefaultOAuth2User에서 사용자 ID로 사용
OAuth2UserService.java
package com.Kkrap.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
//스프링 시큐리티의 DefaultOAuth2UserService를 확장하여 사용자 정보를 가져오고 처리하는 역할
//OAuth2 클라이언트의 사용자 정보를 가져오기 위한 기본 서비스 클래스 DefaultOAuth2UserService
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
Map<String, Object> attributes = oAuth2User.getAttributes();
// 로그 출력
logger.info("카카오로부터 불러온 사용자 정보: {}", attributes);
System.out.println("registrationId :" + registrationId);
System.out.println("userNameAttributeName :" + userNameAttributeName);
System.out.println("attributes :" + attributes);
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")),
attributes,
userNameAttributeName);
}
}
이렇게 하면 결과가 눈에 보일 것이다.
- 이제 코드를 작성 파일 구조는 이렇게 됨
이제 SecurityConfig를 해보자
먼저 코드 설명
HttpSecurity 설정
- http: 이 객체는 웹 기반 보안을 설정하는 데 사용. Spring Security의 주요 설정은 HttpSecurity를 통해 이루어진다
CSRF 설정
- csrf(csrf -> csrf.disable()): CSRF(Cross-Site Request Forgery) 보호 기능을 비활성화. CSRF는 웹 애플리케이션에서 세션 탈취 공격을 방지하는 보안 기능
요청 권한 설정 authorizeHttpRequests
authorizeHttpRequests(authorize -> authorize
: HTTP 요청에 대한 권한 설정을 정의
- requestMatchers("/login", "/oauth2/**").permitAll(): /login 및 /oauth2/** 경로에 대한 접근을 모든 사용자에게 허용 즉, 이 경로에 대한 요청은 인증되지 않은 사용자도 접근할 수 있습니다.
- anyRequest().authenticated(): 위에서 명시한 경로 외의 모든 요청은 인증된 사용자만 접근할 수 있도록 제한
OAuth2 로그인 설정
- oauth2Login(oauth2 -> oauth2: OAuth2 로그인을 설정
- loginPage("/login"): 사용자가 로그인하지 않은 상태에서 보호된 리소스에 접근할 경우, Spring Security는 사용자를 /login 페이지로 리디렉션합니다. 이 /login 페이지는 로그인 UI를 제공하는 페이지
- userInfoEndpoint(userInfo -> userInfo.userService(oAuth2UserService)): OAuth2 인증이 성공적으로 이루어진 후, 사용자 정보를 가져오는 방법을 설정. oAuth2UserService는 이 과정에서 사용자 정보를 처리
return http.build();
- return http.build();: 이 메서드는 HttpSecurity 설정을 마무리하고, SecurityFilterChain을 반환. Spring Security는 이 필터 체인을 사용하여 요청을 처리
SecurityConfig
package com.Kkrap.Security;
import com.Kkrap.Service.CustomOAuth2UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
//import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Autowired
private CustomOAuth2UserService oAuth2UserService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// .csrf(csrf -> csrf
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// )
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/oauth2/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.userInfoEndpoint(userInfo -> userInfo
.userService(oAuth2UserService)
)
);
return http.build();
}
}
그러면 이제 로그인을 하고 난 후 값을 받아보자
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>hihi</p>
</body>
</html>
SecurityConfig.java
package com.Kkrap.Security;
import com.Kkrap.Service.CustomOAuth2UserService;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
private final CustomOAuth2UserService oAuth2UserService;
public SecurityConfig(CustomOAuth2UserService oAuth2UserService) {
this.oAuth2UserService = oAuth2UserService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.userInfoEndpoint(userInfo -> userInfo
.userService(oAuth2UserService)
)
.successHandler(authenticationSuccessHandler()) // 로그인 성공 시 핸들러 사용
);
return http.build();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Object principal = authentication.getPrincipal();
if (principal instanceof DefaultOAuth2User) {
DefaultOAuth2User defaultOAuth2User = (DefaultOAuth2User) principal;
// 카카오 사용자 정보 추출
Map<String, Object> attributes = defaultOAuth2User.getAttributes();
String id = attributes.get("id").toString(); // 사용자 ID
Map<String, Object> kakaoAccount = (Map<String, Object>) attributes.get("kakao_account");
Map<String, Object> profile = (Map<String, Object>) kakaoAccount.get("profile");
String nickname = profile.get("nickname").toString(); // 사용자 닉네임
String profileImage = profile.get("profile_image_url").toString(); // 프로필 이미지 URL
String email = kakaoAccount.get("email").toString(); // 이메일
// 디버그용 로그 출력
System.out.println("카카오 사용자 ID: " + id);
System.out.println("카카오 사용자 닉네임: " + nickname);
System.out.println("카카오 사용자 이메일: " + email);
System.out.println("카카오 사용자 프로필 이미지 URL: " + profileImage);
} else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid user principal type");
}
}
};
}
}
마지막으로 Service 폴더 안에 밑 코드를 추가해주자
CustomOAuth2UserService.java
package com.Kkrap.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
//스프링 시큐리티의 DefaultOAuth2UserService를 확장하여 사용자 정보를 가져오고 처리하는 역할
//OAuth2 클라이언트의 사용자 정보를 가져오기 위한 기본 서비스 클래스 DefaultOAuth2UserService
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
try {
OAuth2User oAuth2User = super.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
Map<String, Object> attributes = oAuth2User.getAttributes();
// 로그 출력
logger.info("카카오로부터 불러온 사용자 정보: {}", attributes);
System.out.println("registrationId :" + registrationId);
System.out.println("userNameAttributeName :" + userNameAttributeName);
System.out.println("attributes :" + attributes);
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")),
attributes,
userNameAttributeName);
// 사용자 정보 처리 로직
} catch (OAuth2AuthenticationException ex) {
logger.error("OAuth2 authentication error", ex);
throw ex;
}
}
}
반응형