Skip to content

Commit c3eede7

Browse files
authored
Merge pull request #9 from AI-Tutor-2024/revert-8-auth
Revert "[FIX] google auth 수정 및 헤더에 오던 토큰 응답을 body로 변경 - front가 유연한 리다이렉트 하도록…"
2 parents 23cf63d + 816a10a commit c3eede7

File tree

9 files changed

+92
-92
lines changed

9 files changed

+92
-92
lines changed

src/main/java/com/example/ai_tutor/domain/auth/application/AuthService.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.example.ai_tutor.domain.auth.dto.AuthRes;
88
import com.example.ai_tutor.domain.auth.dto.RefreshTokenReq;
99
import com.example.ai_tutor.domain.auth.dto.TokenMapping;
10+
import com.example.ai_tutor.domain.professor.domain.Professor;
1011
import com.example.ai_tutor.domain.professor.domain.repository.ProfessorRepository;
1112
import com.example.ai_tutor.domain.user.domain.User;
1213
import com.example.ai_tutor.domain.user.domain.repository.UserRepository;
@@ -22,6 +23,7 @@
2223
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2324
import org.springframework.security.core.Authentication;
2425
import org.springframework.security.core.context.SecurityContextHolder;
26+
import org.springframework.security.crypto.password.PasswordEncoder;
2527
import org.springframework.stereotype.Service;
2628
import org.springframework.transaction.annotation.Transactional;
2729

@@ -32,7 +34,7 @@
3234
@Transactional(readOnly = true)
3335
public class AuthService {
3436

35-
private final JwtUtil jwtUtil;
37+
private final CustomTokenProviderService customTokenProviderService;
3638
private final AuthenticationManager authenticationManager;
3739

3840
private final TokenRepository tokenRepository;
@@ -47,17 +49,17 @@ public ResponseEntity<?> refresh(RefreshTokenReq tokenRefreshRequest){
4749

4850
Token token = tokenRepository.findByRefreshToken(tokenRefreshRequest.getRefreshToken())
4951
.orElseThrow(InvalidTokenException::new);
50-
Authentication authentication = jwtUtil.getAuthenticationByEmail(token.getUserEmail());
52+
Authentication authentication = customTokenProviderService.getAuthenticationByEmail(token.getUserEmail());
5153

5254
//refresh token 정보 값을 업데이트 한다.
5355
//시간 유효성 확인
5456
TokenMapping tokenMapping;
5557

56-
Long expirationTime = jwtUtil.getExpiration(tokenRefreshRequest.getRefreshToken());
58+
Long expirationTime = customTokenProviderService.getExpiration(tokenRefreshRequest.getRefreshToken());
5759
if(expirationTime > 0){
58-
tokenMapping = jwtUtil.refreshToken(authentication, token.getRefreshToken());
60+
tokenMapping = customTokenProviderService.refreshToken(authentication, token.getRefreshToken());
5961
}else{
60-
tokenMapping = jwtUtil.createToken(authentication);
62+
tokenMapping = customTokenProviderService.createToken(authentication);
6163
}
6264

6365
Token updateToken = token.updateRefreshToken(tokenMapping.getRefreshToken());
@@ -93,15 +95,15 @@ public ResponseEntity<?> signOut(UserPrincipal userPrincipal){
9395
private boolean valid(String refreshToken){
9496

9597
//1. 토큰 형식 물리적 검증
96-
boolean validateCheck = jwtUtil.validateToken(refreshToken);
98+
boolean validateCheck = customTokenProviderService.validateToken(refreshToken);
9799
DefaultAssert.isTrue(validateCheck, "Token 검증에 실패하였습니다.");
98100

99101
//2. refresh token 값을 불러온다.
100102
Optional<Token> token = tokenRepository.findByRefreshToken(refreshToken);
101103
DefaultAssert.isTrue(token.isPresent(), "탈퇴 처리된 회원입니다.");
102104

103105
//3. email 값을 통해 인증값을 불러온다
104-
Authentication authentication = jwtUtil.getAuthenticationByEmail(token.get().getUserEmail());
106+
Authentication authentication = customTokenProviderService.getAuthenticationByEmail(token.get().getUserEmail());
105107
DefaultAssert.isTrue(token.get().getUserEmail().equals(authentication.getName()), "사용자 인증에 실패하였습니다.");
106108

107109
return true;
@@ -120,7 +122,7 @@ public ResponseEntity<?> signIn(SignInReq signInReq) {
120122
);
121123
SecurityContextHolder.getContext().setAuthentication(authentication);
122124

123-
TokenMapping tokenMapping = jwtUtil.createToken(authentication);
125+
TokenMapping tokenMapping = customTokenProviderService.createToken(authentication);
124126
Token token = Token.builder()
125127
.refreshToken(tokenMapping.getRefreshToken())
126128
.userEmail(tokenMapping.getUserEmail())

src/main/java/com/example/ai_tutor/domain/auth/application/CustomDefaultOAuth2UserService.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
public class CustomDefaultOAuth2UserService extends DefaultOAuth2UserService {
2828

2929
private final UserRepository userRepository;
30+
private PasswordEncoder passwordEncoder;
3031

3132
@Override
3233
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
@@ -58,20 +59,22 @@ private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2
5859
log.info("New user registered: {}", user);
5960
}
6061

61-
log.info("OAuth2 user processed: {}", user);
62+
log.debug("OAuth2 user processed: {}", user);
6263
return UserPrincipal.create(user, oAuth2User.getAttributes());
6364
}
6465

6566
private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
6667
User user = User.builder()
6768
.name(oAuth2UserInfo.getName())
6869
.email(oAuth2UserInfo.getEmail())
69-
.password("oauth-only")
70+
.password(passwordEncoder.encode(oAuth2UserInfo.getId()))
7071
.provider(Provider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()))
7172
.providerId(oAuth2UserInfo.getId())
7273
.build();
7374

74-
return userRepository.save(user);
75+
User savedUser = userRepository.save(user);
76+
log.debug("New user registered: {}", savedUser);
77+
return savedUser;
7578
}
7679

7780
private User updateExistingUser(User user, OAuth2UserInfo oAuth2UserInfo) {

src/main/java/com/example/ai_tutor/domain/auth/application/JwtUtil.java renamed to src/main/java/com/example/ai_tutor/domain/auth/application/CustomTokenProviderService.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.jsonwebtoken.security.Keys;
99
import lombok.extern.slf4j.Slf4j;
1010
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.beans.factory.annotation.Value;
1112
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1213
import org.springframework.security.core.userdetails.UserDetails;
1314
import org.springframework.security.core.Authentication;
@@ -18,55 +19,48 @@
1819

1920
@Slf4j
2021
@Service
21-
public class JwtUtil {
22+
public class CustomTokenProviderService {
2223

2324
@Autowired
2425
private OAuth2Config oAuth2Config;
2526

2627
@Autowired
2728
private CustomUserDetailsService customUserDetailsService;
2829

29-
30-
31-
public TokenMapping createToken(Authentication authentication) {
30+
public TokenMapping refreshToken(Authentication authentication, String refreshToken) {
3231
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
33-
3432
Date now = new Date();
3533

3634
Date accessTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getAccessTokenExpirationMsec());
37-
Date refreshTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getRefreshTokenExpirationMsec());
3835

3936
String secretKey = oAuth2Config.getAuth().getTokenSecret();
40-
4137
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
4238
Key key = Keys.hmacShaKeyFor(keyBytes);
4339

4440
String accessToken = Jwts.builder()
45-
.setSubject(userPrincipal.getEmail())
41+
.setSubject(Long.toString(userPrincipal.getId()))
4642
.setIssuedAt(new Date())
4743
.setExpiration(accessTokenExpiresIn)
4844
.signWith(key, SignatureAlgorithm.HS512)
4945
.compact();
5046

51-
String refreshToken = Jwts.builder()
52-
.setExpiration(refreshTokenExpiresIn)
53-
.signWith(key, SignatureAlgorithm.HS512)
54-
.compact();
55-
5647
return TokenMapping.builder()
5748
.userEmail(userPrincipal.getEmail())
5849
.accessToken(accessToken)
5950
.refreshToken(refreshToken)
6051
.build();
6152
}
6253

63-
public TokenMapping refreshToken(Authentication authentication, String refreshToken) {
54+
public TokenMapping createToken(Authentication authentication) {
6455
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
56+
6557
Date now = new Date();
6658

6759
Date accessTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getAccessTokenExpirationMsec());
60+
Date refreshTokenExpiresIn = new Date(now.getTime() + oAuth2Config.getAuth().getRefreshTokenExpirationMsec());
6861

6962
String secretKey = oAuth2Config.getAuth().getTokenSecret();
63+
7064
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
7165
Key key = Keys.hmacShaKeyFor(keyBytes);
7266

@@ -77,6 +71,11 @@ public TokenMapping refreshToken(Authentication authentication, String refreshTo
7771
.signWith(key, SignatureAlgorithm.HS512)
7872
.compact();
7973

74+
String refreshToken = Jwts.builder()
75+
.setExpiration(refreshTokenExpiresIn)
76+
.signWith(key, SignatureAlgorithm.HS512)
77+
.compact();
78+
8079
return TokenMapping.builder()
8180
.userEmail(userPrincipal.getEmail())
8281
.accessToken(accessToken)

src/main/java/com/example/ai_tutor/global/config/security/SecurityConfig.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
import com.example.ai_tutor.global.config.security.handler.CustomSimpleUrlAuthenticationFailureHandler;
77
import com.example.ai_tutor.global.config.security.handler.CustomSimpleUrlAuthenticationSuccessHandler;
88
import com.example.ai_tutor.global.config.security.token.CustomAuthenticationEntryPoint;
9-
import com.example.ai_tutor.global.config.security.token.JwtAuthenticationFilter;
9+
import com.example.ai_tutor.global.config.security.token.CustomOncePerRequestFilter;
1010
import lombok.RequiredArgsConstructor;
1111
import org.springframework.context.annotation.Bean;
1212
import org.springframework.context.annotation.Configuration;
1313
import org.springframework.http.HttpMethod;
1414
import org.springframework.security.authentication.AuthenticationManager;
1515
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
16+
import org.springframework.security.config.Customizer;
1617
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
1718
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1819
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -29,6 +30,8 @@
2930
import java.util.Arrays;
3031
import java.util.List;
3132

33+
import static org.springframework.security.config.Customizer.withDefaults;
34+
3235
@RequiredArgsConstructor
3336
@Configuration
3437
@EnableWebSecurity
@@ -46,8 +49,8 @@ public PasswordEncoder passwordEncoder() {
4649
}
4750

4851
@Bean
49-
public JwtAuthenticationFilter customOncePerRequestFilter() {
50-
return new JwtAuthenticationFilter();
52+
public CustomOncePerRequestFilter customOncePerRequestFilter() {
53+
return new CustomOncePerRequestFilter();
5154
}
5255

5356
@Bean

src/main/java/com/example/ai_tutor/global/config/security/handler/CustomSimpleUrlAuthenticationFailureHandler.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,30 @@
77
import jakarta.servlet.http.HttpServletRequest;
88
import jakarta.servlet.http.HttpServletResponse;
99
import lombok.RequiredArgsConstructor;
10-
import lombok.extern.slf4j.Slf4j;
1110
import org.springframework.security.core.AuthenticationException;
1211
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
1312
import org.springframework.stereotype.Component;
1413
import org.springframework.web.util.UriComponentsBuilder;
1514

1615
import java.io.IOException;
17-
import java.util.Arrays;
1816

1917
import static com.example.ai_tutor.domain.auth.domain.repository.CustomAuthorizationRequestRepository.REDIRECT_URI_PARAM_COOKIE_NAME;
2018

2119
@RequiredArgsConstructor
2220
@Component
23-
@Slf4j
2421
public class CustomSimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
2522
private final CustomAuthorizationRequestRepository customAuthorizationRequestRepository;
2623

2724
@Override
2825
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
29-
log.error("OAuth2 로그인 실패: {}", exception.getMessage());
30-
31-
log.info("Request URI: {}", request.getRequestURI());
32-
log.info("Query String: {}", request.getQueryString());
33-
log.info("Cookies: {}", Arrays.toString(request.getCookies()));
34-
3526
String targetUrl = CustomCookie.getCookie(request, REDIRECT_URI_PARAM_COOKIE_NAME)
3627
.map(Cookie::getValue)
3728
.orElse(("/"));
3829

3930
targetUrl = UriComponentsBuilder.fromUriString(targetUrl)
40-
.queryParam("error", "invalid_request")
41-
.build()
42-
.toUriString();
31+
.queryParam("error", exception.getLocalizedMessage())
32+
.build().toUriString();
33+
4334
customAuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
4435

4536
getRedirectStrategy().sendRedirect(request, response, targetUrl);
Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.example.ai_tutor.global.config.security.handler;
22

3-
import com.example.ai_tutor.domain.auth.application.JwtUtil;
3+
import com.example.ai_tutor.domain.auth.application.CustomTokenProviderService;
44
import com.example.ai_tutor.domain.auth.domain.Token;
55
import com.example.ai_tutor.domain.auth.domain.repository.CustomAuthorizationRequestRepository;
66
import com.example.ai_tutor.domain.auth.domain.repository.TokenRepository;
@@ -13,7 +13,6 @@
1313
import jakarta.servlet.http.HttpServletRequest;
1414
import jakarta.servlet.http.HttpServletResponse;
1515
import lombok.RequiredArgsConstructor;
16-
import lombok.extern.slf4j.Slf4j;
1716
import org.springframework.security.core.Authentication;
1817
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
1918
import org.springframework.stereotype.Component;
@@ -25,66 +24,65 @@
2524

2625
import static com.example.ai_tutor.domain.auth.domain.repository.CustomAuthorizationRequestRepository.REDIRECT_URI_PARAM_COOKIE_NAME;
2726

28-
@Slf4j
2927
@RequiredArgsConstructor
3028
@Component
3129
public class CustomSimpleUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
3230

33-
private final JwtUtil jwtUtil; // JWT 생성 유틸
31+
private final CustomTokenProviderService customTokenProviderService;
32+
private final OAuth2Config oAuth2Config;
3433
private final TokenRepository tokenRepository;
3534
private final CustomAuthorizationRequestRepository customAuthorizationRequestRepository;
3635

3736
@Override
38-
public void onAuthenticationSuccess(HttpServletRequest request,
39-
HttpServletResponse response,
40-
Authentication authentication) throws IOException, ServletException {
37+
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
38+
DefaultAssert.isAuthentication(!response.isCommitted());
4139

42-
// 이미 응답이 전송된 상태인지 확인
43-
if (response.isCommitted()) {
44-
log.warn("Response already committed.");
45-
return;
46-
}
40+
String targetUrl = determineTargetUrl(request, response, authentication);
4741

48-
// 1) JWT 토큰 생성
49-
TokenMapping tokenMapping = jwtUtil.createToken(authentication);
42+
TokenMapping token = customTokenProviderService.createToken(authentication);
43+
CustomCookie.addCookie(response, "Authorization", "Bearer_" + token.getAccessToken(), (int) oAuth2Config.getAuth().getAccessTokenExpirationMsec());
44+
CustomCookie.addCookie(response, "Refresh_Token", "Bearer_" + token.getRefreshToken(), (int) oAuth2Config.getAuth().getRefreshTokenExpirationMsec());
5045

51-
// 2) RefreshToken 저장 (DB or Redis etc.)
46+
clearAuthenticationAttributes(request, response);
47+
getRedirectStrategy().sendRedirect(request, response, targetUrl);
48+
}
49+
50+
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
51+
Optional<String> redirectUri = CustomCookie.getCookie(request, REDIRECT_URI_PARAM_COOKIE_NAME).map(Cookie::getValue);
52+
53+
DefaultAssert.isAuthentication( !(redirectUri.isPresent() && !isAuthorizedRedirectUri(redirectUri.get())) );
54+
55+
String targetUrl = redirectUri.orElse(getDefaultTargetUrl());
56+
57+
TokenMapping tokenMapping = customTokenProviderService.createToken(authentication);
5258
Token token = Token.builder()
5359
.userEmail(tokenMapping.getUserEmail())
5460
.refreshToken(tokenMapping.getRefreshToken())
5561
.build();
5662
tokenRepository.save(token);
5763

58-
// 3) JSON으로 응답
59-
response.setContentType("application/json;charset=UTF-8");
60-
response.setCharacterEncoding("UTF-8");
61-
62-
String jsonResponse = String.format(
63-
"{\"accessToken\":\"%s\",\"refreshToken\":\"%s\"}",
64-
tokenMapping.getAccessToken(),
65-
tokenMapping.getRefreshToken()
66-
);
67-
68-
// 4) OAuth2 인증 과정에서 사용된 쿠키/세션 정리
69-
clearAuthenticationAttributes(request, response);
70-
customAuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
71-
72-
// 5) 최종 응답
73-
response.getWriter().write(jsonResponse);
74-
response.getWriter().flush();
75-
76-
log.info("OAuth2 로그인 성공. JWT 토큰 발급 후 JSON으로 응답 완료.");
64+
return UriComponentsBuilder.fromUriString(targetUrl)
65+
.queryParam("token", tokenMapping.getAccessToken())
66+
.build().toUriString();
7767
}
7868

79-
/**
80-
* 원래 SimpleUrlAuthenticationSuccessHandler에는
81-
* clearAuthenticationAttributes(HttpServletRequest request) 만 있으므로,
82-
* 필요시 우리가 (request, response) 시그니처의 메서드로 확장.
83-
* 내부에서는 부모 메서드(super) 호출해서 세션 정리만 수행.
84-
*/
8569
protected void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response) {
86-
// 부모 클래스가 세션의 "SPRING_SECURITY_LAST_EXCEPTION" 제거해줌
8770
super.clearAuthenticationAttributes(request);
71+
customAuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response);
8872
}
89-
}
9073

74+
private boolean isAuthorizedRedirectUri(String uri) {
75+
URI clientRedirectUri = URI.create(uri);
76+
77+
return oAuth2Config.getOauth2().getAuthorizedRedirectUris()
78+
.stream()
79+
.anyMatch(authorizedRedirectUri -> {
80+
URI authorizedURI = URI.create(authorizedRedirectUri);
81+
if(authorizedURI.getHost().equalsIgnoreCase(clientRedirectUri.getHost())
82+
&& authorizedURI.getPort() == clientRedirectUri.getPort()) {
83+
return true;
84+
}
85+
return false;
86+
});
87+
}
88+
}

0 commit comments

Comments
 (0)