Skip to content

Commit c50ae4f

Browse files
committed
Fix: OAuth2 state는 더 이상 쿠키에 저장하지 않고 Redis에 짧은 TTL로 저장하도록 변경
- 브라우저의 서드파티 쿠키 차단으로 인한 authorization_request_not_found가 반복되는 문제를 제거
1 parent 5e8e256 commit c50ae4f

File tree

4 files changed

+99
-163
lines changed

4 files changed

+99
-163
lines changed

src/main/java/wonjun/stiky/auth/config/HttpCookieOAuth2AuthorizationRequestRepository.java

Lines changed: 0 additions & 160 deletions
This file was deleted.

src/main/java/wonjun/stiky/auth/config/OAuth2SuccessHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler
2323

2424
private final JwtTokenProvider jwtTokenProvider;
2525
private final RedisTemplate<String, Object> redisTemplate;
26-
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
26+
private final RedisOAuth2AuthorizationRequestRepository authorizationRequestRepository;
2727

2828
@Value("${openapi.client-url}")
2929
private String url;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package wonjun.stiky.auth.config;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import java.util.Base64;
6+
import java.util.Objects;
7+
import java.util.concurrent.TimeUnit;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.data.redis.core.RedisTemplate;
10+
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
11+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
12+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
13+
import org.springframework.stereotype.Component;
14+
import org.springframework.util.SerializationUtils;
15+
16+
@Component
17+
@RequiredArgsConstructor
18+
public class RedisOAuth2AuthorizationRequestRepository implements
19+
AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
20+
21+
private static final String KEY_PREFIX = "OAUTH2_AUTH_REQUEST:";
22+
private static final long EXPIRE_SECONDS = 180;
23+
24+
private final RedisTemplate<String, Object> redisTemplate;
25+
26+
@Override
27+
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
28+
return findAuthorizationRequest(stateFrom(request));
29+
}
30+
31+
@Override
32+
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
33+
HttpServletResponse response) {
34+
if (authorizationRequest == null) {
35+
removeAuthorizationRequest(request, response);
36+
return;
37+
}
38+
39+
String state = authorizationRequest.getState();
40+
if (state == null) {
41+
return;
42+
}
43+
44+
redisTemplate.opsForValue()
45+
.set(buildKey(state), serialize(authorizationRequest), EXPIRE_SECONDS, TimeUnit.SECONDS);
46+
}
47+
48+
@Override
49+
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request,
50+
HttpServletResponse response) {
51+
String state = stateFrom(request);
52+
if (state == null) {
53+
return null;
54+
}
55+
56+
String key = buildKey(state);
57+
Object serialized = redisTemplate.opsForValue().get(key);
58+
redisTemplate.delete(key);
59+
return deserialize(serialized);
60+
}
61+
62+
private OAuth2AuthorizationRequest findAuthorizationRequest(String state) {
63+
if (state == null) {
64+
return null;
65+
}
66+
67+
Object serialized = redisTemplate.opsForValue().get(buildKey(state));
68+
return deserialize(serialized);
69+
}
70+
71+
private String stateFrom(HttpServletRequest request) {
72+
if (request == null) {
73+
return null;
74+
}
75+
return request.getParameter(OAuth2ParameterNames.STATE);
76+
}
77+
78+
private String buildKey(String state) {
79+
return KEY_PREFIX + state;
80+
}
81+
82+
private String serialize(OAuth2AuthorizationRequest authorizationRequest) {
83+
byte[] bytes = Objects.requireNonNull(SerializationUtils.serialize(authorizationRequest));
84+
return Base64.getEncoder().encodeToString(bytes);
85+
}
86+
87+
private OAuth2AuthorizationRequest deserialize(Object serialized) {
88+
if (!(serialized instanceof String value)) {
89+
return null;
90+
}
91+
92+
byte[] bytes = Base64.getDecoder().decode(value);
93+
return (OAuth2AuthorizationRequest) SerializationUtils.deserialize(bytes);
94+
}
95+
96+
}

src/main/java/wonjun/stiky/global/config/SecurityConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
2020
import wonjun.stiky.auth.config.CustomOAuth2UserService;
2121
import wonjun.stiky.auth.config.CustomUserDetailsService;
22-
import wonjun.stiky.auth.config.HttpCookieOAuth2AuthorizationRequestRepository;
22+
import wonjun.stiky.auth.config.RedisOAuth2AuthorizationRequestRepository;
2323
import wonjun.stiky.auth.config.JwtAuthenticationFilter;
2424
import wonjun.stiky.auth.config.JwtTokenProvider;
2525
import wonjun.stiky.auth.config.OAuth2SuccessHandler;
@@ -32,7 +32,7 @@ public class SecurityConfig {
3232
private final CustomOAuth2UserService customOAuth2UserService;
3333
private final CustomUserDetailsService customUserDetailsService;
3434
private final OAuth2SuccessHandler oAuth2SuccessHandler;
35-
private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;
35+
private final RedisOAuth2AuthorizationRequestRepository authorizationRequestRepository;
3636
private final JwtTokenProvider jwtTokenProvider;
3737

3838
@Value("${cors.allowed-origins}")

0 commit comments

Comments
 (0)