모바일일 경우에는 OAuth 연동을 모바일쪽에서 직접 구현 -> 해당 정보를 백앤드로 전달 해주었다. 웹페이지의 경우에도 그렇게 할 수 있으나 클라이언트 측에 키값이 존재한다는 점이 불안 요소가 있다. (물론 이를 보완할 수도 있다. 키값만 db에 저장한다든지, 서버가 전달 해준다든지 등등)
서버에서 OAuth를 연동할 경우 장점은,
- 키값 안전하게 보호
- 각기 다른 플랫폼에 OAuth 연동해도 연동이 일괄적으로 간편
- 이부분은 클라이언트 쪽에선 진짜...하나 추가될 때 마다 괴로웠음
- 스프링부트에서는 설정만 추가하고 필요하면 코드만 살짝 바꾸면 된다.
일단, 구글 기준으로 스프링부트에서 연동하는 내용을 정리해 둔다.
우선순위 부터 보자면
- 각 플랫폼의 dev 사이트에서 프로젝트 생성
- 프로젝트에서 OAuth 연동을 위한 콜백 Uri 지정
- builld.gradle 에 라이브러리 추가
- 발급된 클라이언트ID, 비밀번호를 스프링부트의 환경변수에 삽입 및 application.yaml 설정
- SecurityConfig 설정
- 연동 Service 코드 구현
- JWT 연동하여 토큰 발급 후 프론트엔드에 전달
각 플랫폼의 dev 혹은 console 사이트에서 프로젝트 생성
이 부분 설정법은 금방 쉽게 찾을 수 있기 때문에 설정을 해주어야 한다는 사실만 기억하면 되고 여기선 다루지 않는다.
다만 아래 2가지는 기억하는게 좋다.
- 프로젝트에서 클라이언트 생성시, 웹 애플리케이션으로 생성할 것
- 웹 애플리케이션에서 승인된 리디렉션 URI에 주소값을 넣을것
- 구글 예 : 개발중일경우 : http://localhost:8080/login/oauth2/code/google
- /login/oauth2/code/google 이게 항상 일치해야 한다.
- 구글 예 : 개발중일경우 : http://localhost:8080/login/oauth2/code/google
Build.gradle 추가
- Oauth2 를 위한 라이브러리가 있다.
- 각 플랫폼에 요청을 위한 uri가 security에 고정되어 있다.
- 구글 : http://localhost:8080/oauth2/authorization/google
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
application.yaml 수정
- Spring Security의 oauth2 항목을 설정해주면 된다.
- 다른 플랫폼과 연동시 단순히 registration, provider 쪽에 항목을 추가해주면 된다.
- 구글의 경우 provider 부분은 필수는 아니고 선택사항이다. 그러나 kakao 같은 경운 필수로 넣어야 한다.
- provider 에 있는 각 주소들은 해당 플랫폼에서 제공하는 주소이니, 값의 변화가 없다.
- scope 하단에 서버가 가져올 정보를 명시한다. 각 제공처 마다 다르니 주의할것.
- 구글의 경우 profile 을 추가 하면 이름을 가져 올 수 있다.
- 이를 가져오기 위해선 구글 콘솔 -> OAuth 에서 해당 정보를 가져오겠다는 것을 설정을 통해 해 주어야 한다.
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_SECRET}
scope:
- email
- profile
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: sub
OAuthService 구현
- OAuth2UserService 를 implement 하여 구현한다.
- 이메일이 없을경우 처리는 비즈니스 로직 부분이다.
- 필요하다면 profile 등 을 통해 이름을 가져올 수도 있다. (application.yaml 설정 및 각 서비스 도메인 설정 필요)
@Service
@RequiredArgsConstructor
public class OAuthService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService();
OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest);
Map<String,Object> attributes = oAuth2User.getAttributes();
String email = (String) attributes.get("email");
....//비즈니스 로직 구현 부분
return new DefaultOAuth2User(
Set.of(new SimpleGrantedAuthority("ROLE_USER")),
attributes,
"sub"
);
}
}
SecurityConfig 설정 추가
- oauth2Login 설정만 해주면 된다.
- userService 에 위에 설정한 서비스를 넣어준다.
- 따로 csrf 설정이나 authorizeHttpRequests.requestMatchers 설정을 하지 않아도, Spring Security에서 별도로 다른 filter를 태운다
- 로그인 성공, 실패 콜백을 등록하여, 로그인 성공시 JWT 토큰 발급 등을 할 수 있도록 할 필요가 있다.
....
private final OAuthService oAuthService;
private final OAuthSuccessHandler oAuthSuccessHandler;
private final OAuthFailureHandler oAuthFailureHandler;
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.oauth2Login(oauth -> oauth.userInfoEndpoint(
info -> info.userService(oAuthService)
.successHandler(oAuthSuccessHandler)
.failureHandler(jwtLoginFailureHandler)
))
....
로그인 성공, 실패 핸들러
- 성공 핸들러 예만 첨부한다.
- 요점은, 토큰 발급 후 프론트엔드 에서 원하는 주소로 re-direct 해주어야 한다는 점이다. (실패시 에도 마찬가지)
- redirect 할 때 토큰을 바로 노출하는건 좋지 않고, 쿠키에 넣어 전달 -> 클라이언트가 쿠키에서 가져와 처리 하는게 좋다 (프론트와 협의 필요)
@Component
@RequiredArgsConstructor
public class OAuth2SuccessHandler
implements AuthenticationSuccessHandler {
private final JwtProvider jwtProvider;
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String token = jwtProvider.createToken(authentication);
response.sendRedirect(
"http://frontend.com/login?token=" + token
);
}
}
접근 테스트 (구글)
- http://localhost:8080/login/oauth2/code/google 주소를 통해 브라우저로 접속하여 테스트 한다. (개발환경)
- 프론트엔드 페이지가 이미 구현완료 되었다면 프론트엔드를 통해 완료한다.
'Backend > SpringBoot' 카테고리의 다른 글
| [Spring Security] XSS 방어 (0) | 2026.02.11 |
|---|---|
| [Entity] GeneratedValue 대신 TSID 사용하기 (0) | 2026.02.10 |
| Cursor 기반 Paging 추가 정리(QueryDsl 기준) (0) | 2026.02.03 |
| 임시 비번 발급 후 비교할때 주의점(PasswordEncoder) (0) | 2026.02.02 |
| Hexagonal Architecture 일부적용 및 느낀점 (0) | 2026.01.30 |