2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

View File

@@ -0,0 +1,260 @@
아래는 "스프링 시큐리티" 책의 7장에 포함될 "OAuth2 기본 개념", "구글 등 소셜 로그인 구현", 그리고 "커스텀 OAuth2 클라이언트 설정"에 대한 내용입니다. 개념을 명확히 하고 실습 가능한 예제를 포함해 실무 적용성을 높였습니다.
---
### 7장. OAuth2와 소셜 로그인
#### 7.1 OAuth2 기본 개념
**OAuth2**는 인증(Authentication)과 권한 부여(Authorization)를 위한 표준 프로토콜로, 사용자가 자신의 자격 증명을 직접 공유하지 않고도 제3자 애플리케이션이 리소스에 접근할 수 있도록 합니다. 소셜 로그인(구글, 페이스북 등)이나 API 인증에 널리 사용됩니다.
##### OAuth2의 주요 구성 요소
- **Resource Owner**: 리소스를 소유한 사용자(예: 구글 계정 소유자).
- **Client**: 리소스에 접근하려는 애플리케이션(우리의 스프링 앱).
- **Authorization Server**: 사용자를 인증하고 토큰을 발급하는 서버(예: 구글 인증 서버).
- **Resource Server**: 보호된 리소스를 제공하는 서버(예: 구글 API).
- **Access Token**: 클라이언트가 리소스에 접근할 때 사용하는 키.
##### 인증 흐름 (Authorization Code Grant)
가장 흔히 사용되는 흐름으로, 소셜 로그인에 적합합니다:
1. 사용자가 클라이언트에서 "구글로 로그인" 버튼을 클릭.
2. 클라이언트가 사용자를 Authorization Server로 리다이렉트.
3. 사용자가 로그인 후 권한을 승인하면 Authorization Code가 클라이언트로 반환.
4. 클라이언트가 코드를 Access Token으로 교환.
5. Access Token으로 Resource Server에서 사용자 정보를 가져옴.
스프링 시큐리티는 OAuth2를 쉽게 통합할 수 있도록 `spring-security-oauth2-client` 모듈을 제공하며, 최소한의 설정으로 소셜 로그인을 구현할 수 있습니다.
#### 7.3 구글, 깃허브 등 소셜 로그인 구현
스프링 시큐리티를 사용하면 구글, 깃허브 같은 소셜 로그인을 빠르게 구현할 수 있습니다. 여기서는 구글 로그인을 예로 설명합니다.
##### 1. 의존성 추가
`pom.xml`에 다음 의존성을 추가:
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
```
##### 2. 구글 OAuth2 클라이언트 등록
1. [Google Cloud Console](https://console.cloud.google.com)에서 프로젝트 생성.
2. "OAuth 2.0 클라이언트 ID" 생성:
- 애플리케이션 유형: 웹 애플리케이션.
- 리다이렉트 URI: `http://localhost:8080/login/oauth2/code/google`.
3. 클라이언트 ID와 클라이언트 비밀번호(Secret)를 발급받음.
##### 3. 설정 파일 작성
`application.yml`에 구글 OAuth2 설정 추가:
```yaml
spring:
security:
oauth2:
client:
registration:
google:
client-id: your-google-client-id
client-secret: your-google-client-secret
scope: profile, email
provider:
google:
authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
token-uri: https://oauth2.googleapis.com/token
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
user-name-attribute: email
```
##### 4. SecurityConfig 설정
```java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login") // 커스텀 로그인 페이지
.defaultSuccessUrl("/home") // 로그인 성공 시 이동
)
.logout(logout -> logout.logoutSuccessUrl("/"));
return http.build();
}
}
```
##### 5. 로그인 페이지 제작
`src/main/resources/templates/login.html`:
```html
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>로그인</h2>
<a href="/oauth2/authorization/google">구글로 로그인</a>
</body>
</html>
```
- `/oauth2/authorization/google`은 스프링 시큐리티가 자동 생성한 구글 로그인 경로.
##### 6. 사용자 정보 확인
로그인 성공 후 `Principal` 객체로 사용자 정보를 가져올 수 있습니다:
```java
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping("/home")
public String home(@AuthenticationPrincipal OAuth2User oAuth2User, Model model) {
model.addAttribute("name", oAuth2User.getAttribute("name"));
model.addAttribute("email", oAuth2User.getAttribute("email"));
return "home";
}
}
```
`home.html`:
```html
<!DOCTYPE html>
<html>
<body>
<h1>환영합니다, <span th:text="${name}"></span>!</h1>
<p>이메일: <span th:text="${email}"></span></p>
<a href="/logout">로그아웃</a>
</body>
</html>
```
##### 깃허브 로그인 추가
`application.yml`에 깃허브 설정 추가:
```yaml
spring:
security:
oauth2:
client:
registration:
github:
client-id: your-github-client-id
client-secret: your-github-client-secret
scope: read:user
```
로그인 링크: `<a href="/oauth2/authorization/github">깃허브로 로그인</a>`.
##### 동작 원리
1. 사용자가 구글 로그인 링크를 클릭하면 구글 인증 서버로 리다이렉트.
2. 구글에서 인증 후 리다이렉트 URI로 코드를 반환.
3. 스프링 시큐리티가 코드를 토큰으로 교환하고, 사용자 정보를 가져와 `OAuth2User`로 저장.
4. 인증 성공 시 `/home`으로 이동.
#### 7.4 커스텀 OAuth2 클라이언트 설정
스프링 시큐리티의 기본 설정으로 지원되지 않는 제공자(예: 네이버, 카카오)나 고급 요구사항을 처리하려면 커스텀 OAuth2 클라이언트를 설정해야 합니다.
##### 네이버 로그인 예제
1. **네이버 개발자 센터**에서 클라이언트 ID와 Secret 발급.
2. `application.yml`에 추가:
```yaml
spring:
security:
oauth2:
client:
registration:
naver:
client-id: your-naver-client-id
client-secret: your-naver-client-secret
redirect-uri: "{baseUrl}/login/oauth2/code/naver"
authorization-grant-type: authorization_code
scope: name, email
provider:
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response # 네이버는 사용자 정보가 'response' 객체에 포함됨
```
3. **커스텀 User Service**:
네이버의 사용자 정보 형식이 구글과 다르므로 `OAuth2UserService`를 커스터마이징:
```java
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2User oAuth2User = super.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
if ("naver".equals(registrationId)) {
Map<String, Object> response = (Map<String, Object>) oAuth2User.getAttributes().get("response");
return new org.springframework.security.oauth2.core.user.DefaultOAuth2User(
oAuth2User.getAuthorities(),
response, // 네이버의 사용자 정보
"id" // 고유 식별자
);
}
return oAuth2User;
}
}
```
4. **SecurityConfig에 등록**:
```java
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, CustomOAuth2UserService customOAuth2UserService) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.defaultSuccessUrl("/home")
);
return http.build();
}
}
```
##### 커스터마이징 포인트
- **토큰 처리**: `.tokenEndpoint()`로 커스텀 토큰 요청 설정.
- **사용자 매핑**: 데이터베이스에 사용자 정보를 저장하거나, 추가 속성을 매핑.
- **에러 처리**: `.failureHandler()`로 인증 실패 시 커스텀 로직 추가.
##### 실습 예제
1. 네이버 로그인 버튼 추가: `<a href="/oauth2/authorization/naver">네이버로 로그인</a>`.
2. 로그인 후 반환된 사용자 정보(이름, 이메일 등)를 화면에 출력.
3. 데이터베이스에 신규 사용자를 등록하는 로직 추가.
---
위 내용은 OAuth2의 기본 개념과 구글 소셜 로그인 구현, 커스텀 OAuth2 클라이언트 설정 방법을 설명했습니다. 추가적인 설정이나 예제가 필요하면 말씀해 주세요!