Skip to content

Commit 62fe99c

Browse files
authored
Merge pull request #202 from CodIN-INU/develop
chore : localhost Cookie 설정
2 parents f12324b + 1f1f564 commit 62fe99c

15 files changed

Lines changed: 111 additions & 49 deletions

File tree

src/main/java/inu/codin/codin/common/config/SecurityConfig.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
3333
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
3434
import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository;
35-
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
3635
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3736
import org.springframework.security.oauth2.core.OAuth2Error;
3837
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
3938
import org.springframework.security.oauth2.core.user.OAuth2User;
4039
import org.springframework.security.web.SecurityFilterChain;
4140
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
4241
import org.springframework.security.web.authentication.logout.LogoutFilter;
42+
import org.springframework.web.context.request.RequestContextListener;
4343
import org.springframework.web.cors.CorsConfiguration;
4444
import org.springframework.web.cors.CorsConfigurationSource;
4545
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -156,6 +156,11 @@ public AuthenticationManager authenticationManager(HttpSecurity http) throws Exc
156156
return authenticationManagerBuilder.build();
157157
}
158158

159+
@Bean
160+
public RequestContextListener requestContextListener() {
161+
return new RequestContextListener();
162+
}
163+
159164
@Bean
160165
public RoleHierarchy roleHierarchy() {
161166
return RoleHierarchyImpl.fromHierarchy("ROLE_ADMIN > ROLE_MANAGER > ROLE_USER");
@@ -210,6 +215,10 @@ public CorsConfigurationSource corsConfigurationSource() {
210215
// Admin 권한 URL
211216
private static final String[] ADMIN_AUTH_PATHS = {
212217
"/v3/api/test4",
218+
"/swagger-ui/**",
219+
"/v3/api-docs/**",
220+
"/v3/api-docs",
221+
"/swagger-resources/**"
213222
};
214223

215224
// Manager 권한 URL
Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
package inu.codin.codin.common.security.service;
22

3-
import inu.codin.codin.common.exception.NotFoundException;
4-
import inu.codin.codin.common.security.dto.SignUpAndLoginRequestDto;
5-
import inu.codin.codin.domain.user.dto.request.UserProfileRequestDto;
6-
import inu.codin.codin.domain.user.entity.UserEntity;
7-
import inu.codin.codin.domain.user.exception.UserCreateFailException;
8-
import inu.codin.codin.domain.user.exception.UserNicknameDuplicateException;
93
import inu.codin.codin.domain.user.repository.UserRepository;
104
import inu.codin.codin.infra.s3.S3Service;
5+
import jakarta.servlet.http.HttpServletRequest;
116
import jakarta.servlet.http.HttpServletResponse;
127
import lombok.RequiredArgsConstructor;
138
import lombok.extern.slf4j.Slf4j;
149
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1510
import org.springframework.security.core.context.SecurityContextHolder;
1611
import org.springframework.security.core.userdetails.UserDetails;
1712
import org.springframework.security.core.userdetails.UserDetailsService;
18-
import org.springframework.security.oauth2.core.user.OAuth2User;
19-
import org.springframework.web.multipart.MultipartFile;
20-
21-
import java.time.LocalDateTime;
22-
import java.util.List;
23-
import java.util.Optional;
2413

2514
@RequiredArgsConstructor
2615
@Slf4j
@@ -29,14 +18,15 @@ public abstract class AbstractAuthService {
2918
protected final S3Service s3Service;
3019
protected final JwtService jwtService;
3120
protected final UserDetailsService userDetailsService;
21+
protected final HttpServletRequest request;
3222

3323
protected void issueJwtToken(String identifier, HttpServletResponse response) {
3424
jwtService.deleteToken(response);
3525
UserDetails userDetails = userDetailsService.loadUserByUsername(identifier);
3626
UsernamePasswordAuthenticationToken authToken =
3727
new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
3828
SecurityContextHolder.getContext().setAuthentication(authToken);
39-
jwtService.createToken(response);
29+
jwtService.createToken(request, response);
4030
}
4131

4232
}

src/main/java/inu/codin/codin/common/security/service/AppleAuthService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import inu.codin.codin.domain.user.entity.UserStatus;
99
import inu.codin.codin.domain.user.repository.UserRepository;
1010
import inu.codin.codin.infra.s3.S3Service;
11+
import jakarta.servlet.http.HttpServletRequest;
1112
import jakarta.servlet.http.HttpServletResponse;
1213
import lombok.extern.slf4j.Slf4j;
1314
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -21,8 +22,8 @@
2122
@Slf4j
2223
public class AppleAuthService extends AbstractAuthService implements Oauth2AuthService {
2324

24-
public AppleAuthService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService) {
25-
super(userRepository, s3Service, jwtService, userDetailsService);
25+
public AppleAuthService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService, HttpServletRequest request) {
26+
super(userRepository, s3Service, jwtService, userDetailsService, request);
2627
}
2728

2829
@Override

src/main/java/inu/codin/codin/common/security/service/AuthCommonService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import inu.codin.codin.domain.user.exception.UserNicknameDuplicateException;
99
import inu.codin.codin.domain.user.repository.UserRepository;
1010
import inu.codin.codin.infra.s3.S3Service;
11+
import jakarta.servlet.http.HttpServletRequest;
1112
import jakarta.servlet.http.HttpServletResponse;
12-
import lombok.RequiredArgsConstructor;
1313
import lombok.extern.slf4j.Slf4j;
1414
import org.springframework.security.core.userdetails.UserDetailsService;
1515
import org.springframework.security.oauth2.core.user.OAuth2User;
@@ -25,8 +25,8 @@
2525
public class AuthCommonService extends AbstractAuthService {
2626

2727

28-
public AuthCommonService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService) {
29-
super(userRepository, s3Service, jwtService, userDetailsService);
28+
public AuthCommonService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService, HttpServletRequest request) {
29+
super(userRepository, s3Service, jwtService, userDetailsService, request);
3030
}
3131

3232
public void completeUserProfile(UserProfileRequestDto userProfileRequestDto, MultipartFile userImage, HttpServletResponse response) {

src/main/java/inu/codin/codin/common/security/service/CustomOAuth2UserService.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import inu.codin.codin.common.security.dto.AccessEmailProperties;
5+
import jakarta.servlet.http.HttpSession;
56
import lombok.RequiredArgsConstructor;
67
import lombok.extern.slf4j.Slf4j;
78
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
@@ -11,10 +12,9 @@
1112
import org.springframework.security.oauth2.core.user.OAuth2User;
1213
import org.springframework.stereotype.Service;
1314
import org.springframework.util.AntPathMatcher;
15+
import org.springframework.web.context.request.RequestContextHolder;
16+
import org.springframework.web.context.request.ServletRequestAttributes;
1417

15-
/**
16-
* OAuth2 로그인 후 사용자 정보를 가로채고 이메일을 검증하는 로직
17-
*/
1818
@Service
1919
@RequiredArgsConstructor
2020
@Slf4j
@@ -23,15 +23,21 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
2323
private final AccessEmailProperties accessEmailProperties;
2424
private final AntPathMatcher pathMatcher = new AntPathMatcher();
2525

26+
private final String OAUTH2_ACCESS_TOKEN = "oauth2_access_token";
27+
2628
@Override
2729
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
2830
OAuth2User oAuth2User = super.loadUser(userRequest);
2931

32+
// 요청에서 HttpSession 가져오기
33+
HttpSession httpSession = getSession();
34+
3035
// Get User Email
3136
String email = oAuth2User.getAttribute("email");
3237
log.info("email: {}", email);
3338

3439
if (email == null) {
40+
httpSession.setAttribute(OAUTH2_ACCESS_TOKEN, userRequest.getAccessToken().getTokenValue());
3541
OAuth2Error oauth2Error = new OAuth2Error(
3642
"email_not_found",
3743
"이메일 정보를 찾을 수 없습니다.",
@@ -47,6 +53,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
4753

4854
// Only Allow @inu.ac.kr
4955
if (!email.trim().endsWith("@inu.ac.kr")) {
56+
httpSession.setAttribute(OAUTH2_ACCESS_TOKEN, userRequest.getAccessToken().getTokenValue());
5057
OAuth2Error oauth2Error = new OAuth2Error(
5158
"invalid_email_domain",
5259
"허용되지 않은 이메일 도메인입니다. @inu.ac.kr 이어야합니다",
@@ -57,4 +64,12 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
5764

5865
return oAuth2User;
5966
}
67+
68+
public HttpSession getSession() {
69+
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
70+
if (attr == null) {
71+
throw new IllegalStateException("No thread-bound request found. RequestContextListener 또는 RequestContextFilter를 설정하세요.");
72+
}
73+
return attr.getRequest().getSession();
74+
}
6075
}

src/main/java/inu/codin/codin/common/security/service/GoogleAuthService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import inu.codin.codin.domain.user.entity.UserStatus;
99
import inu.codin.codin.domain.user.repository.UserRepository;
1010
import inu.codin.codin.infra.s3.S3Service;
11+
import jakarta.servlet.http.HttpServletRequest;
1112
import jakarta.servlet.http.HttpServletResponse;
12-
import lombok.RequiredArgsConstructor;
1313
import lombok.extern.slf4j.Slf4j;
1414
import org.springframework.security.core.userdetails.UserDetailsService;
1515
import org.springframework.security.oauth2.core.user.OAuth2User;
@@ -23,8 +23,8 @@
2323
public class GoogleAuthService extends AbstractAuthService implements Oauth2AuthService {
2424

2525

26-
public GoogleAuthService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService) {
27-
super(userRepository, s3Service, jwtService, userDetailsService);
26+
public GoogleAuthService(UserRepository userRepository, S3Service s3Service, JwtService jwtService, UserDetailsService userDetailsService, HttpServletRequest request) {
27+
super(userRepository, s3Service, jwtService, userDetailsService, request);
2828
}
2929

3030
@Override

src/main/java/inu/codin/codin/common/security/service/JwtService.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public class JwtService {
4040
* 최초 로그인 시 Access Token, Refresh Token 발급
4141
* @param response
4242
*/
43-
public void createToken(HttpServletResponse response) {
44-
createBothToken(response);
43+
public void createToken(HttpServletRequest request, HttpServletResponse response) {
44+
createBothToken(request, response);
4545
log.info("[createToken] Access Token, Refresh Token 발급 완료");
4646
}
4747

@@ -68,38 +68,43 @@ public void reissueToken(HttpServletRequest request, HttpServletResponse respons
6868
SecurityContextHolder.getContext().setAuthentication(authentication);
6969
}
7070

71-
reissueToken(refreshToken, response);
71+
reissueToken(refreshToken, request, response);
7272
}
7373

7474
/**
7575
* Refresh Token을 이용하여 Access Token, Refresh Token 재발급
7676
* @param refreshToken
7777
* @param response
7878
*/
79-
public void reissueToken(String refreshToken, HttpServletResponse response) {
79+
public void reissueToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) {
8080
if (!jwtTokenProvider.validateRefreshToken(refreshToken)) {
8181
log.error("[reissueToken] Refresh Token이 유효하지 않습니다. : {}", refreshToken);
8282
throw new JwtException(SecurityErrorCode.INVALID_TOKEN, "Refresh Token이 유효하지 않습니다.");
8383
}
8484

85-
createBothToken(response);
85+
createBothToken(request, response);
8686
log.info("[reissueToken] Access Token, Refresh Token 재발급 완료");
8787
}
8888

8989
/**
9090
* Access Token, Refresh Token 생성
9191
*/
92-
private void createBothToken(HttpServletResponse response) {
92+
private void createBothToken(HttpServletRequest request, HttpServletResponse response) {
9393
// 새로운 Access Token 발급
9494
var authentication = SecurityContextHolder.getContext().getAuthentication();
9595
JwtTokenProvider.TokenDto newToken = jwtTokenProvider.createToken(authentication);
9696

97+
String domain = null;
98+
if (!request.getHeader("Origin").split("//")[1].startsWith("localhost")){
99+
domain = BASERURL.split("//")[1];
100+
}
101+
97102
Cookie jwtCookie = new Cookie("access_token", newToken.getAccessToken());
98103
jwtCookie.setHttpOnly(true); // JavaScript에서 접근 불가
99104
jwtCookie.setSecure(true); // HTTPS 환경에서만 전송
100105
jwtCookie.setPath("/"); // 모든 요청에 포함
101106
jwtCookie.setMaxAge(60 * 60); // 1시간 유지
102-
jwtCookie.setDomain(BASERURL.split("//")[1]);
107+
jwtCookie.setDomain(domain);
103108
jwtCookie.setAttribute("SameSite", "None");
104109
response.addCookie(jwtCookie);
105110

@@ -109,7 +114,7 @@ private void createBothToken(HttpServletResponse response) {
109114
refreshCookie.setSecure(true);
110115
refreshCookie.setPath("/");
111116
refreshCookie.setMaxAge(7 * 24 * 60 * 60); // 7일
112-
refreshCookie.setDomain(BASERURL.split("//")[1]);
117+
refreshCookie.setDomain(domain);
113118
refreshCookie.setAttribute("SameSite", "None");
114119
response.addCookie(refreshCookie);
115120

src/main/java/inu/codin/codin/common/security/util/OAuth2LoginFailureHandler.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inu.codin.codin.common.util.CookieUtil;
77
import jakarta.servlet.http.HttpServletRequest;
88
import jakarta.servlet.http.HttpServletResponse;
9+
import jakarta.servlet.http.HttpSession;
910
import lombok.RequiredArgsConstructor;
1011
import lombok.extern.slf4j.Slf4j;
1112
import org.springframework.beans.factory.annotation.Value;
@@ -14,20 +15,24 @@
1415
import org.springframework.security.oauth2.core.OAuth2Error;
1516
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
1617
import org.springframework.stereotype.Component;
18+
import org.springframework.web.client.RestTemplate;
1719

1820
import java.io.IOException;
1921

2022
@Component
2123
@Slf4j
2224
@RequiredArgsConstructor
2325
public class OAuth2LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
26+
private final RestTemplate restTemplate = new RestTemplate();
2427
private final ObjectMapper objectMapper = new ObjectMapper();
2528

2629
private final JwtService jwtService;
30+
private final HttpSession httpSession;
2731

2832
@Value("${server.domain}")
2933
private String BASEURL;
3034
public final static String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request";
35+
private final String OAUTH2_ACCESS_TOKEN = "oauth2_access_token";
3136

3237

3338
@Override
@@ -52,15 +57,27 @@ public void onAuthenticationFailure(HttpServletRequest request,
5257

5358
log.error("[OAuth2LoginFailureHandler] {}", responseBody);
5459

60+
// 🔹 Access Token 가져오기 (요청 파라미터에서 추출)
61+
String accessToken = (String) httpSession.getAttribute(OAUTH2_ACCESS_TOKEN);
62+
63+
if (accessToken != null && !accessToken.isEmpty()) {
64+
String revokeUrl = "https://accounts.google.com/o/oauth2/revoke?token=" + accessToken;
65+
try {
66+
restTemplate.getForObject(revokeUrl, String.class);
67+
log.info("[OAuth2LoginFailureHandler] Google Access Token revoked successfully.");
68+
} catch (Exception e) {
69+
log.error("[OAuth2LoginFailureHandler] Failed to revoke Google Access Token: {}", e.getMessage());
70+
}
71+
} else {
72+
log.warn("[OAuth2LoginFailureHandler] No access token found in request.");
73+
}
74+
5575
removeAllToken(request, response);
5676

57-
response.getWriter().write(responseBody);
5877
if (errorCode == null)
5978
getRedirectStrategy().sendRedirect(request, response, BASEURL+"/login");
6079
else {
6180
getRedirectStrategy().sendRedirect(request, response, BASEURL + "/login?error=" + errorCode);
62-
String logoutUrl = "https://accounts.google.com/Logout";
63-
response.sendRedirect(logoutUrl);
6481
}
6582
}
6683

src/main/java/inu/codin/codin/domain/lecture/domain/review/repository/ReviewRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface ReviewRepository extends MongoRepository<ReviewEntity, ObjectId
3232
})
3333
Emotion getEmotionsCountByRanges(ObjectId lectureId);
3434

35-
int countAllByLectureIdAndDeletedAtIsNotNull(ObjectId lectureId);
35+
int countByLectureId(ObjectId lectureId);
3636

3737
Page<ReviewEntity> getAvgRatingByLectureId(ObjectId lectureId, PageRequest pageRequest);
3838

src/main/java/inu/codin/codin/domain/lecture/domain/review/service/ReviewService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void updateRating(ObjectId lectureId){
6666
.orElseThrow(() -> new NotFoundException("강의 정보를 찾을 수 없습니다."));
6767
double starRating = reviewRepository.getAvgRatingByLectureId(lectureId);
6868
Emotion emotion = reviewRepository.getEmotionsCountByRanges(lectureId).changeToPercentage();
69-
int participants = reviewRepository.countAllByLectureIdAndDeletedAtIsNotNull(lectureId);
69+
int participants = reviewRepository.countByLectureId(lectureId);
7070
lectureEntity.updateReviewRating(starRating, participants, emotion);
7171
lectureRepository.save(lectureEntity);
7272
}

0 commit comments

Comments
 (0)