개요
이번에는 구글 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를 사용해서 구현해볼 것이다.
'Spring > Security' 카테고리의 다른 글
Spring Security + OAuth2 + JWT 를 사용한 로그인, 로그아웃 구현 - 2 (0) | 2024.08.13 |
---|---|
Spring Security + OAuth2 + JWT 를 사용한 로그인, 로그아웃 구현 - 1편 (0) | 2024.08.09 |
Spring Security + jwt 토큰 (with 쿠키) :: http-only 설정방법 (0) | 2024.08.02 |
Spring : JWT를 왜 사용해야 할까? (0) | 2024.07.27 |
Spring Security + OAuth2 + Session 를 사용한 로그인, 로그아웃 구현 - Kakao, Naver (0) | 2024.07.26 |