참고
Spring Boot 게시판 OAuth 2.0 구글 로그인 구현
Google Oauth api 설정을 이미 진행한 뒤 내용을 진행합니다!
dependencies {
...
// OAuth2
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:2.6.2"
}
의존성 설정과 함께
spring:
# google
security:
oauth2:
client:
registration:
google:
client-id: {clientId}
client-secret: {secret}
scope: profile, email
application.yml
파일도 설정해주었다.
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
@Column(nullable = false, length = 30, unique = true)
private String username; //아이디
@Column(nullable = false, unique = true)
private String nickname;
@Column(length = 300)
private String password; // 추후에 자체 회원가입 만들 때 필요
@Column(nullable = false)
private String email;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
}
@Getter
@RequiredArgsConstructor
public enum Role {
USER("ROLE_USER"),
ADMIN("ROLE_USER"),
SOCIAL("ROLE_USER"), // OAuth
;
private final String value;
}
import com.molu.molu.service.member.CustomOAuth2UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/", "/v1/**", "/login/**", "/h2-console", "/h2-console/**", "/docs/index.html").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.successHandler(new SimpleUrlAuthenticationSuccessHandler("/login/oauth2"))
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
}
security 설정을 하나씩 확인해보자
.csrf().disable()
기존 security의 Cross-Site Request Forgery 시스템은 여러 사이트의 api 호출을 받아야하는 어플리케이션에는 필요 없으므로 disable 처리 해준다.
.headers().frameOptions().disable()
h2-console 사용하기 위해서 설정해주어야한다.
antMatchers()
특정 url을 지정할 수 있다.
successHandler()
로그인 성공시 endpoint url을 지정할 수 있다.
userService()
OAuth 로그인을 성공한 이후에 받아온 데이터를 핸들링 하는 부분이다.
@RequiredArgsConstructor
@Service
@Slf4j
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final MemberRepository memberRepository;
@Override
@Transactional
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
// OAuth2 서비스 id (google, naver, kakao ...)
String registrationId = userRequest.getClientRegistration().getRegistrationId();
// OAuth2 로그인 진행 시 키값
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
// TODO 회원 가입 로직
Member member = memberRepository.findByEmail(attributes.getEmail())
.orElse(attributes.saveMember());
memberRepository.save(member);
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(member.getRole().getValue())),
attributes.getAttributes(),
attributes.getNameAttributeKey()
);
}
}
CustomOAuth2UserService
로직 구현은 다음과 같다.
OAuth2에서 모두 구현해 두었기때문에 우리는 따로 설정할 필요가 없다.
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class OAuthAttributes {
private Map<String, Object> attributes;
private String nameAttributeKey;
private String username;
private String nickname;
private String email;
private Role role;
public static OAuthAttributes of(String registrationId, String userNameAttributeName, Map<String, Object> attributes){
if (registrationId.equals("google")){
return ofGoogle(userNameAttributeName, attributes);
}
return null;
}
private static OAuthAttributes ofGoogle(String userNameAttributeName, Map<String, Object> attributes) {
return OAuthAttributes.builder()
.username(attributes.get("email").toString())
.email(attributes.get("email").toString())
.nickname(attributes.get("name").toString())
.attributes(attributes)
.nameAttributeKey(userNameAttributeName)
.build();
}
public Member saveMember(){
return Member.builder()
.username(email)
.email(email)
.nickname(nickname)
.role(Role.SOCIAL)
.build();
}
}
OAuthAttributes
에서는 앞으로 OAuth에서 사용될 데이터를 만들어주는 부분이다.
@RestController
@RequestMapping("/login/oauth2")
public class LoginController {
@GetMapping("")
public ResponseEntity<CommonApi<String>> loginGoogle(){
return ResponseEntity.ok(CommonApi.<String>builder()
.resultCode(ResultCode.SUCCESS)
.resultType(ResultType.NONE)
.data("성공")
.build());
}
}
최종적으로 로그인이 완료된 후 도착할 endpoint url 부분이다. 이쪽에서 마지막 JWT 토큰을 발급해주는 부분을 구현해주면 될거 같다.