spring-oauth2-client를 사용해서 소셜로그인하면 OAuth2에 필요한 정보를 세션에 임시저장함 -> JSESSION
그런데 oauth2 인증절차 중 다른 분산 서버로 요청 가면, 세션값이 없기 때문에 무한 요청 -> 503 에러(too many redirect)
따라서 oauth2 인증 저장소를 세션 -> 쿠키로 변경
HttpSessionOAuth2AuthorizationRequestRepository 를 만들어서 사용.
이렇게 되면 임시운용하던 Sticky Sessions 제거하고 브라우저 쿠키 이용해서 stateless 환경 구축.
마이그레이션 하기 전, Sticky Sessions를 공부해보자.
Sticky Sessions
로드 밸런서의 설정을 통해 사용자의 요청이 처음 세션이 저장한 서버로만 가도록 설정
단점
1. 트래픽이 서버에 몰릴 수 있어서 로드밸런싱 의미 퇴색
2. 특정 서버에 장애가 발생 시, 해당 서버 세션 전부 사라지는 단점
여러 WAS의 Session이 모두 공유되도록 하는 방법
하지만 이러한 방식을 이용할 때, 서버가 많아진다면
하나의 세션이 생성될 때 모든 서버에 세션을 생성.(서버의 메모리 부하 커짐❗❗)
⇒ 이러한 문제를 해결하는 것이 Redis Session Clustering
모든 서버가 같은 Redis Session 저장소를 바라보게 하는 방법.
왜 Redis 일까?
왜 RDB를 사용하지 않고 Redis를 사용할까요.
Redis의 장점
- Session 저장소 같은 경우는 모든 인가 요청 시에 사용, 저장소 접근이 잦다.
- 저장소가 RDB라면 디스크 I/O 작업을 하지 않기 때문에 메모리 낭비 줄일 수 있다.
Redis는 인메모리 기반으로 디스크 I/O 작업을 하지 않기 때문에 메모리 낭비 줄인다.
Spring Data Redis → 프레임워크 → Lettuce를 함께 의존성 추가
implementation 'org.springframework.session:spring-session-data-redis'
끝.
다시 JSESSION 문제 해결 해보자
JSESSION 생성 이유 복습
1. OAuth 로그인 기본 동작
- Spring Security OAuth2 로그인 과정에서 기본적으로 세션을 사용해 인증 요청 정보를 저장합니다.
도움받았습니다!
[개인 프로젝트(15)] Stateless OAuth2.0 Social Login (Spring Security + JWT + OAuth 2.0)
Spring Security OAuth2 Login은 따로 설정하지 않는다면, 인증 서버로 인가 코드 발급을 요청을 보내기 위해 필요한 정보를 캡슐화한 객체를 세션에 저장한다.JWT 기반 인증을 구현하고, 세션을 사용하
velog.io
코드는
https://velog.io/@summer_today/10.3-Spring-Security를-이용한-OAuth2-구현-1
10.3 Spring Security를 이용한 OAuth2 구현 - OAuth2 로직 구현
의존성 추가 OAuth2를 사용하기 위해 build.gradle 파일에 의존성을 추가한다. 쿠키 관리 클래스 구현하기 쿠키 생성•삭제를 담당하는 쿠키 관리 클래스를 구현한다. addCookie() 요청값(이름, 값, 만료
velog.io
이 분 참고.
----------------------------------------
해결방법
이를 변경하려면 HttpSessionOAuth2AuthorizationRequestRepository 대신 stateless 방식인
HttpCookieOAuth2AuthorizationRequestRepository 를 사용.
authorizationRequestRepository()
OAuth 2.0 인가 요청을 저장하고 관리하는 데 사용되는 리포지토리를 설정한다. 이 리포지토리는 인증 흐름 중에 사용되는 인가 요청을 저장하고, 필요할 때 해당 리포리티지에서 검색한다. 이 경우 oAuth2AuthorizationRequestBasedOnCookieRepository()를 사용하여 쿠키에 기반한 인가 요청 리포지토리를 설정한다.
oAuth2AuthorizationRequestBasedOnCookieRepository()는 OAuth 2.0 인증 요청을 쿠키를 기반으로 저장하고 관리하는 데 사용되는 리포지토리를 생성하는 메서드이다. 해당 리포지토리는 사용자가 OAuth 2.0 인증 프로세스를 시작할 때 생성된 인증 요청을 저장하고, 나중에 해당 요청을 검색하여 처리하는 데 사용된다.
oAuth2AuthorizationRequestBasedOnCookieRepository()는 사용자가 외부 인증 서비스로부터 리다이렉션 되기 전에 애플리케이션이 사용자의 인증 요청을 저장하고 관리하는 데 사용된다. 해당 메서드를 통해 생성된 리포지토리는 인증 요청을 쿠키에 저장하여 사용자의 브라우저에 유지하고, 사용자가 다시 애플리케이션으로 리디렉션 되었을 때 해당 요청을 검색할 수 있다.
저장소 구현
OAuth2에 필요한 정보를 세션이 아닌 쿠키에 저장해서 쓸 수 있도록 인증 요청과 관련된 상태를 저장할 저장소를 구현한다.
권한 인증 흐름에서 클라이언트의 요청을 유지하는 데 사용되는 OAuth2AuthorizationRequestBasedOnCookieRepository 클래스를 구현해 쿠키를 사용해 OAuth의 정보를 가져오고 저장하는 로직을 작성한다.
해당 코드는 OAuth2.0 인증 요청을 쿠키를 사용하여 저장하고 검색하기 위한 Spring Security의 AuthorizationRequestRepository를 구현한 클래스이다.
<변경 전 코드>
// 기존: OAuth2 로그인 설정
http.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.successHandler(customSuccessHandler)
);
<변경 후 코드>
// 새로운: OAuth2 로그인 설정
http.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(authorization -> authorization
.authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository) // Cookie 기반으로 요청 상태 저장
)
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService))
.successHandler(customSuccessHandler)
);
쿠키 기반으로 저장하도록 변경.
해당 파일 추가
HttpCookieOAuth2AuthorizationRequestRepository.java
package com.cpplab.security.repository;
import com.cpplab.security.utils.Aes256;
import com.cpplab.security.utils.CookieUtil;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
@Slf4j
@Component
public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
public static final String OAUTH2_COOKIE_NAME = "MYAPP_OAUTH2_AUTHORIZATION_REQUEST";
public static final Duration OAUTH_COOKIE_EXPIRY = Duration.ofMinutes(5);
@Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
return getCookie(request);
}
@Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response) {
if (authorizationRequest == null) {
removeAuthorizationRequest(request, response);
return;
}
CookieUtil.addCookie(response, OAUTH2_COOKIE_NAME, encrypt(authorizationRequest), OAUTH_COOKIE_EXPIRY);
}
@Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) {
OAuth2AuthorizationRequest oAuth2AuthorizationRequest = getCookie(request);
CookieUtil.removeCookie(request, response, OAUTH2_COOKIE_NAME);
return oAuth2AuthorizationRequest;
}
private OAuth2AuthorizationRequest getCookie(HttpServletRequest request) {
return CookieUtil.getCookie(request, OAUTH2_COOKIE_NAME)
.map(this::decrypt)
.orElse(null);
}
private String encrypt(OAuth2AuthorizationRequest authorizationRequest) {
byte[] bytes = SerializationUtils.serialize(authorizationRequest);
return Aes256.encrypt(bytes);
}
private OAuth2AuthorizationRequest decrypt(Cookie cookie) {
byte[] bytes = Aes256.decrypt(cookie.getValue().getBytes(StandardCharsets.UTF_8));
return (OAuth2AuthorizationRequest) SerializationUtils.deserialize(bytes);
}
}
사용할 때 AES256 으로 (암호화, 복호화)해서 사용하며 5분 후 자동으로 expire.
이제 JSESSION 생기지 않는 거 확인완료
기대효과: ELB에 Sticky Sessions 없어도 소셜 로그인 가능함.
저의 깃허브 참고하세요~
https://github.com/ktb-cpplab/cpplab-be
GitHub - ktb-cpplab/cpplab-be
Contribute to ktb-cpplab/cpplab-be development by creating an account on GitHub.
github.com
'Solo Project > 소셜로그인+JWT' 카테고리의 다른 글
[최종] Spring OAuth2.0 client + 카카오 (로그인, 로그아웃) + JWT (0) | 2024.08.31 |
---|---|
[ERROR] CORS + SOP + preflight (0) | 2024.08.06 |
[카카오 소셜 로그인] 1. 순서도 (4) | 2024.07.23 |