From 61c4940275685cdd2f3f749c05bc8d054a58414e Mon Sep 17 00:00:00 2001 From: ohhamma Date: Fri, 4 Oct 2024 18:40:04 +0900 Subject: [PATCH 1/3] KL-183/feat: logout and disable cookie --- .../domain/token/dao/TokenRepository.java | 2 ++ .../domain/token/service/TokenService.java | 2 ++ .../token/service/TokenServiceImpl.java | 7 +++++ .../config/security/CustomLogoutHandler.java | 28 +++++++++++++++++ .../security/CustomLogoutSuccessHandler.java | 30 +++++++++++++++++++ .../config/security/SecurityConfig.java | 19 ++++++++---- .../security/TokenAuthenticationFilter.java | 17 ++--------- .../java/taco/klkl/global/util/TokenUtil.java | 23 +++++++++++++- 8 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java create mode 100644 src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java diff --git a/src/main/java/taco/klkl/domain/token/dao/TokenRepository.java b/src/main/java/taco/klkl/domain/token/dao/TokenRepository.java index 044bf24e..b8a8d7f1 100644 --- a/src/main/java/taco/klkl/domain/token/dao/TokenRepository.java +++ b/src/main/java/taco/klkl/domain/token/dao/TokenRepository.java @@ -13,4 +13,6 @@ public interface TokenRepository extends JpaRepository { Optional findByName(final String name); Optional findByAccessToken(final String accessToken); + + void deleteByName(final String name); } diff --git a/src/main/java/taco/klkl/domain/token/service/TokenService.java b/src/main/java/taco/klkl/domain/token/service/TokenService.java index ad14044b..3f7e3604 100644 --- a/src/main/java/taco/klkl/domain/token/service/TokenService.java +++ b/src/main/java/taco/klkl/domain/token/service/TokenService.java @@ -12,4 +12,6 @@ public interface TokenService { Token findByAccessTokenOrThrow(final String accessToken); void updateToken(final String accessToken, final Token token); + + void deleteToken(final String name); } diff --git a/src/main/java/taco/klkl/domain/token/service/TokenServiceImpl.java b/src/main/java/taco/klkl/domain/token/service/TokenServiceImpl.java index 4112edb7..3a2888ce 100644 --- a/src/main/java/taco/klkl/domain/token/service/TokenServiceImpl.java +++ b/src/main/java/taco/klkl/domain/token/service/TokenServiceImpl.java @@ -38,7 +38,14 @@ public Token findByAccessTokenOrThrow(final String accessToken) { } @Override + @Transactional public void updateToken(final String accessToken, final Token token) { token.update(token.getRefreshToken(), accessToken); } + + @Override + @Transactional + public void deleteToken(final String name) { + tokenRepository.deleteByName(name); + } } diff --git a/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java b/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java new file mode 100644 index 00000000..def84d90 --- /dev/null +++ b/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java @@ -0,0 +1,28 @@ +package taco.klkl.global.config.security; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Component; +import taco.klkl.domain.token.service.TokenService; + +@Component +@RequiredArgsConstructor +public class CustomLogoutHandler implements LogoutHandler { + + private final TokenService tokenService; + + @Override + public void logout( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication + ) { + if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { + tokenService.deleteToken(userDetails.getUsername()); + } + } +} diff --git a/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java b/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java new file mode 100644 index 00000000..a0a1207b --- /dev/null +++ b/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java @@ -0,0 +1,30 @@ +package taco.klkl.global.config.security; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; +import taco.klkl.global.util.TokenUtil; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class CustomLogoutSuccessHandler implements LogoutSuccessHandler { + + private final TokenUtil tokenUtil; + + @Override + public void onLogoutSuccess( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication + ) throws IOException { + tokenUtil.clearAccessTokenCookie(response); + response.setStatus(HttpStatus.NO_CONTENT.value()); + response.getWriter().flush(); + } +} \ No newline at end of file diff --git a/src/main/java/taco/klkl/global/config/security/SecurityConfig.java b/src/main/java/taco/klkl/global/config/security/SecurityConfig.java index b86d7969..199c98ad 100644 --- a/src/main/java/taco/klkl/global/config/security/SecurityConfig.java +++ b/src/main/java/taco/klkl/global/config/security/SecurityConfig.java @@ -38,6 +38,8 @@ public class SecurityConfig { private final OAuth2SuccessHandler oAuth2SuccessHandler; private final CustomAccessDeniedHandler accessDeniedHandler; private final CustomAuthenticationEntryPoint authenticationEntryPoint; + private final CustomLogoutHandler customLogoutHandler; + private final CustomLogoutSuccessHandler customLogoutSuccessHandler; private final TokenAuthenticationFilter tokenAuthenticationFilter; @Value("${spring.application.uri}") @@ -63,17 +65,16 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti // disable default login form .formLogin(AbstractHttpConfigurer::disable) - // disable default logout - .logout(AbstractHttpConfigurer::disable) - // disable X-Frame-Options (enable h2-console) .headers((headers) -> headers.contentTypeOptions(contentTypeOptionsConfig -> - headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))) + headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)) + ) // disable session .sessionManagement(sessionManagement -> - sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) // request authentication & authorization .authorizeHttpRequests(authorizeRequests -> @@ -97,6 +98,14 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti .baseUri("/v1/login/oauth2/code/*")) ) + // configure logout + .logout(logout -> logout + .logoutUrl("/v1/logout") + .addLogoutHandler(customLogoutHandler) + .logoutSuccessHandler(customLogoutSuccessHandler) + .permitAll() + ) + // auth exception handling .exceptionHandling(exception -> exception diff --git a/src/main/java/taco/klkl/global/config/security/TokenAuthenticationFilter.java b/src/main/java/taco/klkl/global/config/security/TokenAuthenticationFilter.java index 01f7dc5b..d77f86d3 100644 --- a/src/main/java/taco/klkl/global/config/security/TokenAuthenticationFilter.java +++ b/src/main/java/taco/klkl/global/config/security/TokenAuthenticationFilter.java @@ -1,10 +1,7 @@ package taco.klkl.global.config.security; -import static taco.klkl.global.common.constants.TokenConstants.ACCESS_TOKEN; - import java.io.IOException; import java.util.Arrays; -import java.util.Optional; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -14,7 +11,6 @@ import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -46,7 +42,7 @@ protected void doFilterInternal( FilterChain filterChain ) throws ServletException, IOException { try { - String accessToken = resolveToken(request); + String accessToken = tokenUtil.resolveToken(request); if (tokenProvider.validateToken(accessToken)) { setAuthentication(accessToken); } else { @@ -69,17 +65,8 @@ protected void doFilterInternal( filterChain.doFilter(request, response); } - private void setAuthentication(String accessToken) { + private void setAuthentication(final String accessToken) { Authentication authentication = tokenProvider.getAuthentication(accessToken); SecurityContextHolder.getContext().setAuthentication(authentication); } - - private String resolveToken(HttpServletRequest request) { - return Optional.ofNullable(request.getCookies()) - .flatMap(cookies -> Arrays.stream(cookies) - .filter(cookie -> ACCESS_TOKEN.equals(cookie.getName())) - .findFirst() - .map(Cookie::getValue)) - .orElse(null); - } } diff --git a/src/main/java/taco/klkl/global/util/TokenUtil.java b/src/main/java/taco/klkl/global/util/TokenUtil.java index fbe0b039..4da3a952 100644 --- a/src/main/java/taco/klkl/global/util/TokenUtil.java +++ b/src/main/java/taco/klkl/global/util/TokenUtil.java @@ -2,20 +2,41 @@ import static taco.klkl.global.common.constants.TokenConstants.ACCESS_TOKEN; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.Optional; + @Component public class TokenUtil { + public String resolveToken(HttpServletRequest request) { + return Optional.ofNullable(request.getCookies()) + .flatMap(cookies -> Arrays.stream(cookies) + .filter(cookie -> ACCESS_TOKEN.equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue)) + .orElse(null); + } + public void addAccessTokenCookie(HttpServletResponse response, String accessToken) { Cookie cookie = new Cookie(ACCESS_TOKEN, accessToken); cookie.setHttpOnly(true); - cookie.setSecure(false); // TODO: HTTPS 사용 시 활성화 + cookie.setSecure(true); cookie.setPath("/"); cookie.setMaxAge(3600); response.addCookie(cookie); } + + public void clearAccessTokenCookie(HttpServletResponse response) { + Cookie cookie = new Cookie(ACCESS_TOKEN, null); + cookie.setPath("/"); + cookie.setHttpOnly(true); + cookie.setMaxAge(0); + response.addCookie(cookie); + } } From c6870d5bb3de5e5e6fa802557ec2c3cda53b2009 Mon Sep 17 00:00:00 2001 From: ohhamma Date: Fri, 4 Oct 2024 18:40:17 +0900 Subject: [PATCH 2/3] KL-183/feat: add logout endpoint as user role --- .../java/taco/klkl/global/config/security/SecurityEndpoint.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/taco/klkl/global/config/security/SecurityEndpoint.java b/src/main/java/taco/klkl/global/config/security/SecurityEndpoint.java index 10cbfdd2..3bddcc5d 100644 --- a/src/main/java/taco/klkl/global/config/security/SecurityEndpoint.java +++ b/src/main/java/taco/klkl/global/config/security/SecurityEndpoint.java @@ -41,6 +41,7 @@ public enum SecurityEndpoint { new AntPathRequestMatcher("/v1/me/**"), new AntPathRequestMatcher("/v1/likes/**"), new AntPathRequestMatcher("/v1/notifications/**"), + new AntPathRequestMatcher("/v1/logout/**"), }), ; From 99d941215f153515351ab7a74595e358b70be23c Mon Sep 17 00:00:00 2001 From: ohhamma Date: Fri, 4 Oct 2024 19:07:30 +0900 Subject: [PATCH 3/3] KL-183/fix: fix checkstyle errors --- .../global/config/security/CustomLogoutHandler.java | 7 ++++--- .../config/security/CustomLogoutSuccessHandler.java | 13 +++++++------ src/main/java/taco/klkl/global/util/TokenUtil.java | 8 ++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java b/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java index def84d90..9befe8e6 100644 --- a/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java +++ b/src/main/java/taco/klkl/global/config/security/CustomLogoutHandler.java @@ -1,12 +1,13 @@ package taco.klkl.global.config.security; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import taco.klkl.domain.token.service.TokenService; @Component diff --git a/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java b/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java index a0a1207b..f4579b16 100644 --- a/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java +++ b/src/main/java/taco/klkl/global/config/security/CustomLogoutSuccessHandler.java @@ -1,15 +1,16 @@ package taco.klkl.global.config.security; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; +import java.io.IOException; + import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; -import taco.klkl.global.util.TokenUtil; -import java.io.IOException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import taco.klkl.global.util.TokenUtil; @Component @RequiredArgsConstructor @@ -27,4 +28,4 @@ public void onLogoutSuccess( response.setStatus(HttpStatus.NO_CONTENT.value()); response.getWriter().flush(); } -} \ No newline at end of file +} diff --git a/src/main/java/taco/klkl/global/util/TokenUtil.java b/src/main/java/taco/klkl/global/util/TokenUtil.java index 62e4d529..fcefa1df 100644 --- a/src/main/java/taco/klkl/global/util/TokenUtil.java +++ b/src/main/java/taco/klkl/global/util/TokenUtil.java @@ -2,15 +2,15 @@ import static taco.klkl.global.common.constants.TokenConstants.ACCESS_TOKEN; -import jakarta.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Optional; + import org.springframework.stereotype.Component; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.util.Arrays; -import java.util.Optional; - @Component public class TokenUtil {