Skip to content

Commit

Permalink
Merge pull request #69 from taco-official/KL-183/로그아웃-구현
Browse files Browse the repository at this point in the history
feat(KL-183): logout
  • Loading branch information
ohhamma authored Oct 4, 2024
2 parents 6c8946a + 99d9412 commit 65d3381
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/main/java/taco/klkl/domain/token/dao/TokenRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface TokenRepository extends JpaRepository<Token, Long> {
Optional<Token> findByName(final String name);

Optional<Token> findByAccessToken(final String accessToken);

void deleteByName(final String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface TokenService {
Token findByAccessTokenOrThrow(final String accessToken);

void updateToken(final String accessToken, final Token token);

void deleteToken(final String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package taco.klkl.global.config.security;

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
@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());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package taco.klkl.global.config.security;

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 jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import taco.klkl.global.util.TokenUtil;

@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();
}
}
19 changes: 14 additions & 5 deletions src/main/java/taco/klkl/global/config/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand All @@ -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 ->
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public enum SecurityEndpoint {
new AntPathRequestMatcher("/v1/me/**"),
new AntPathRequestMatcher("/v1/likes/**"),
new AntPathRequestMatcher("/v1/notifications/**"),
new AntPathRequestMatcher("/v1/logout/**"),
}),
;

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -49,7 +45,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 {
Expand All @@ -72,17 +68,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);
}
}
21 changes: 21 additions & 0 deletions src/main/java/taco/klkl/global/util/TokenUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@

import static taco.klkl.global.common.constants.TokenConstants.ACCESS_TOKEN;

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;

@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);
Expand All @@ -19,4 +32,12 @@ public void addAccessTokenCookie(HttpServletResponse response, String accessToke
cookie.setAttribute("SameSite", "Strict");
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);
}
}

0 comments on commit 65d3381

Please sign in to comment.