diff --git a/src/main/java/com/kakaoteck/golagola/config/SecurityConfig.java b/src/main/java/com/kakaoteck/golagola/config/SecurityConfig.java index 7025f61..c6213d6 100644 --- a/src/main/java/com/kakaoteck/golagola/config/SecurityConfig.java +++ b/src/main/java/com/kakaoteck/golagola/config/SecurityConfig.java @@ -1,6 +1,7 @@ package com.kakaoteck.golagola.config; import com.kakaoteck.golagola.security.filter.JwtAuthenticationFilter; +import com.kakaoteck.golagola.security.handler.signout.CustomSignOutProcessHandler; import com.kakaoteck.golagola.security.jwt.JWTFilter; import com.kakaoteck.golagola.security.jwt.JWTUtil; import com.kakaoteck.golagola.security.handler.signin.CustomSuccessHandler; @@ -12,6 +13,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; @@ -34,9 +37,7 @@ public class SecurityConfig { private final CustomOAuth2UserService customOAuth2UserService; private final CustomSuccessHandler customSuccessHandler; private final JWTUtil jwtUtil; - private final JwtAuthenticationFilter jwtAuthFilter; - private final AuthenticationProvider authenticationProvider; - private final LogoutHandler logoutHandler; + private final CustomSignOutProcessHandler customSignOutProcessHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -55,9 +56,6 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { } })); - - - // CSRF 보호 비활성화 http.csrf(AbstractHttpConfigurer::disable); @@ -76,14 +74,14 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { // 로그아웃 설정 http.logout(logout -> logout.logoutUrl("/api/v1/auth/logout") //.addLogoutHandler(logoutHandler) // 지미꺼 - .addLogoutHandler(logoutHandler) // 코이꺼 - - .deleteCookies("JSESSIONID", "Authorization") + .addLogoutHandler(customSignOutProcessHandler) // 코이꺼 + .deleteCookies("JSESSIONID", "Authorization", "RefreshToken") ); // JWT 필터 설정 -// http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); - http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); + http.addFilterBefore(new JWTFilter(jwtUtil), LogoutFilter.class); // 로그아웃 필터전에 jwt필터실행 + http.addFilterBefore(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class); +// http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class); // 경로별 인가 작업 http.authorizeHttpRequests(auth -> auth @@ -97,7 +95,7 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { } private static final String[] WHITE_LIST_URL = { - "/api/v1/auth/**", +// "/api/v1/auth/**", "/v2/api-docs", "/v3/api-docs", "/v3/api-docs/**", diff --git a/src/main/java/com/kakaoteck/golagola/domain/auth/Repository/UserRepository.java b/src/main/java/com/kakaoteck/golagola/domain/auth/Repository/UserRepository.java index a29376c..146da2f 100644 --- a/src/main/java/com/kakaoteck/golagola/domain/auth/Repository/UserRepository.java +++ b/src/main/java/com/kakaoteck/golagola/domain/auth/Repository/UserRepository.java @@ -1,7 +1,10 @@ package com.kakaoteck.golagola.domain.auth.Repository; import com.kakaoteck.golagola.domain.auth.entity.UserEntity; +import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import java.util.Optional; @@ -9,6 +12,12 @@ public interface UserRepository extends JpaRepository { // UserEntity findByUsername(String username); // username을 전달하여 해당하는 엔티티 가져오기(JPA) Optional findByUsername(String username); // 차이가 뭔지 공부하기 + + @Modifying + @Query("UPDATE UserEntity u SET u.refreshToken = :refreshToken, u.loginStatus = :loginStatus WHERE u.username = :username") + void updateRefreshTokenAndLoginStatus(@Param("userName") String username, + @Param("refreshToken") String refreshToken, + @Param("loginStatus") boolean loginStatus); } diff --git a/src/main/java/com/kakaoteck/golagola/domain/auth/dto/CustomOAuth2User.java b/src/main/java/com/kakaoteck/golagola/domain/auth/dto/CustomOAuth2User.java index 7490cab..0747c8d 100644 --- a/src/main/java/com/kakaoteck/golagola/domain/auth/dto/CustomOAuth2User.java +++ b/src/main/java/com/kakaoteck/golagola/domain/auth/dto/CustomOAuth2User.java @@ -46,4 +46,8 @@ public String getUsername() { return userDTO.getUsername(); } + public Long getId() { // id 값을 반환하는 메서드 추가 + return userDTO.getId(); + } + } diff --git a/src/main/java/com/kakaoteck/golagola/domain/auth/dto/UserDTO.java b/src/main/java/com/kakaoteck/golagola/domain/auth/dto/UserDTO.java index c5b7ca5..53ae51d 100644 --- a/src/main/java/com/kakaoteck/golagola/domain/auth/dto/UserDTO.java +++ b/src/main/java/com/kakaoteck/golagola/domain/auth/dto/UserDTO.java @@ -7,7 +7,11 @@ @Setter public class UserDTO { + private Long id; // 엔티티의 id 추가 private String role; private String name; private String username; + private String email; // 엔티티의 email 추가 + private String refreshToken; // 엔티티의 refreshToken 추가 + private boolean loginStatus; // 엔티티의 loginStatus 추가 } diff --git a/src/main/java/com/kakaoteck/golagola/domain/auth/entity/UserEntity.java b/src/main/java/com/kakaoteck/golagola/domain/auth/entity/UserEntity.java index dc94284..f460c3d 100644 --- a/src/main/java/com/kakaoteck/golagola/domain/auth/entity/UserEntity.java +++ b/src/main/java/com/kakaoteck/golagola/domain/auth/entity/UserEntity.java @@ -22,8 +22,8 @@ public class UserEntity { private String role; // 추가 - private String RefreshToken; // JWT 리프레시 토큰 발급 - private boolean LoginStatus; // 로그인 상태처리 + private String refreshToken; // JWT 리프레시 토큰 발급 + private boolean loginStatus; // 로그인 상태처리 diff --git a/src/main/java/com/kakaoteck/golagola/security/handler/signout/CustomSignOutProcessHandler.java b/src/main/java/com/kakaoteck/golagola/security/handler/signout/CustomSignOutProcessHandler.java index 1db7cde..3d531a2 100644 --- a/src/main/java/com/kakaoteck/golagola/security/handler/signout/CustomSignOutProcessHandler.java +++ b/src/main/java/com/kakaoteck/golagola/security/handler/signout/CustomSignOutProcessHandler.java @@ -1,40 +1,47 @@ -//package com.kakaoteck.golagola.security.handler.signout; -// -// -//import jakarta.servlet.http.HttpServletRequest; -//import jakarta.servlet.http.HttpServletResponse; -//import jakarta.transaction.Transactional; -//import lombok.RequiredArgsConstructor; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.security.core.Authentication; -//import org.springframework.security.web.authentication.logout.LogoutHandler; -//import org.springframework.stereotype.Component; -// -//@Component -//@RequiredArgsConstructor -//@Slf4j -//public class CustomSignOutProcessHandler implements LogoutHandler { -//// private final UserRepository userRepository; -//// -//// @Override -//// @Transactional -//// public void logout(HttpServletRequest request, HttpServletResponse response, -//// Authentication authentication) { -//// if (authentication == null) { -//// return; -//// } -//// -//// CustomUserDetails userPrincipal = (CustomUserDetails) authentication.getPrincipal(); -//// -//// userRepository.updateRefreshTokenAndLoginStatus(userPrincipal.getId(), null, false); -//// } -// -// -//} -// -// -// -// -// -// -// +package com.kakaoteck.golagola.security.handler.signout; + + +import com.kakaoteck.golagola.domain.auth.Repository.UserRepository; +import com.kakaoteck.golagola.domain.auth.dto.CustomOAuth2User; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomSignOutProcessHandler implements LogoutHandler { + private final UserRepository userRepository; + + @Override + @Transactional + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + if (authentication == null) { + return; + } + + // 1. 테리코드 +// CustomUserDetails userPrincipal = (CustomUserDetails) authentication.getPrincipal(); +// userRepository.updateRefreshTokenAndLoginStatus(userPrincipal.getId(), null, false); + + // 2. 용우코드 + CustomOAuth2User userPrincipal = (CustomOAuth2User) authentication.getPrincipal(); // CustomOAuth2User 사용 + System.out.println("로그아웃 정보 확인"+ userPrincipal + userPrincipal.getUsername()); + userRepository.updateRefreshTokenAndLoginStatus(userPrincipal.getUsername(), null, false); // UserEntity에서 해당 유저를 찾아서 리프레시 토큰과 로그인 상태를 업데이트 + + } + + +} + + + + + + + diff --git a/src/main/java/com/kakaoteck/golagola/security/jwt/JWTFilter.java b/src/main/java/com/kakaoteck/golagola/security/jwt/JWTFilter.java index 96cebc2..8d3b6d5 100644 --- a/src/main/java/com/kakaoteck/golagola/security/jwt/JWTFilter.java +++ b/src/main/java/com/kakaoteck/golagola/security/jwt/JWTFilter.java @@ -25,6 +25,19 @@ public JWTFilter(JWTUtil jwtUtil) { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // jwt 기간 만료시, 무한 재로그인 방지 로직 + String requestUri = request.getRequestURI(); + if (requestUri.matches("^\\/login(?:\\/.*)?$")) { + + filterChain.doFilter(request, response); + return; + } + if (requestUri.matches("^\\/oauth2(?:\\/.*)?$")) { + + filterChain.doFilter(request, response); + return; + } + // cookie들을 불러온 뒤 Authorization Key에 담긴 쿠키를 찾음 String authorization = null; Cookie[] cookies = request.getCookies(); @@ -38,7 +51,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } } } - // Authorization 헤더 검증 if (authorization == null) { System.out.println("token null"); @@ -59,6 +71,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 토큰에서 username과 role 획득 String username = jwtUtil.getUsername(token); String role = jwtUtil.getRole(token); + System.out.println("jwtfilter jwt확인: " + username + role); // userDTO를 생성하여 값 set UserDTO userDTO = new UserDTO(); @@ -68,13 +81,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // UserDetails에 회원 정보 객체 담기 CustomOAuth2User customOAuth2User = new CustomOAuth2User(userDTO); - // 스프링 시큐리티 인증 토큰 생성 + // 스프링 시큐리티 인증 토큰 생성, 스프링 시큐리티에서 세션을 생성해가지고 토큰을 등록하고 있음. Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, customOAuth2User.getAuthorities()); // 세션에 사용자 등록 SecurityContextHolder.getContext().setAuthentication(authToken); - filterChain.doFilter(request, response); + filterChain.doFilter(request, response); // jwtfilter작업을 다 했기 때문에 다음 필터에게 작업을 넘긴다는 doFilter작업을 진행해주시면 됩니다. } } diff --git a/src/main/java/com/kakaoteck/golagola/service/CustomOAuth2UserService.java b/src/main/java/com/kakaoteck/golagola/service/CustomOAuth2UserService.java index 50f2c9e..c57f6a8 100644 --- a/src/main/java/com/kakaoteck/golagola/service/CustomOAuth2UserService.java +++ b/src/main/java/com/kakaoteck/golagola/service/CustomOAuth2UserService.java @@ -16,7 +16,6 @@ // OAuth2UserRequest: 리소스 서버에서 제공되는 유저정보 @Service public class CustomOAuth2UserService extends DefaultOAuth2UserService { - private final UserRepository userRepository; public CustomOAuth2UserService(UserRepository userRepository) {