Skip to content

Commit

Permalink
Merge pull request #20 from SafeNet-2024/feature/logout
Browse files Browse the repository at this point in the history
LGTM~๐Ÿ‘๐Ÿป
  • Loading branch information
khee2 authored Jun 7, 2024
2 parents c85b9de + 835d7d2 commit ddc4036
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 498 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ out/
!**/src/main/**/out/
!**/src/test/**/out/
src/main/resources/application.properties
src/main/resources/test
src/main/resources/initial_region_data.sql
src/main/resources/update_region_data.sql
### NetBeans ###
/nbproject/private/
/nbbuild/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.SafeNet.Backend.domain.member.dto.*;
import com.SafeNet.Backend.domain.member.entity.UserDetailsImpl;
import com.SafeNet.Backend.domain.member.service.EmailService;
import com.SafeNet.Backend.domain.member.service.JwtBlacklistService;
import com.SafeNet.Backend.domain.member.service.MemberService;
import com.SafeNet.Backend.global.exception.CustomException;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -28,6 +29,7 @@ public class MemberController {

private final MemberService memberService;
private final EmailService mailService;
private final JwtBlacklistService jwtBlacklistService;

@Operation(summary = "ํšŒ์›๊ฐ€์ž…", description = "ํšŒ์›๊ฐ€์ž…์„ ์Šน์ธํ•ฉ๋‹ˆ๋‹ค.")
@PostMapping("/signup")
Expand Down Expand Up @@ -91,7 +93,7 @@ public ResponseEntity<Void> logout( @RequestHeader(name = "ACCESS_TOK
UserDetailsImpl userDetails = (UserDetailsImpl) principal;
String email = userDetails.getUsername();
log.info("ํ† ํฐ์œผ๋กœ๋ถ€ํ„ฐ ์ด๋ฉ”์ผ์„ ์ถ”์ถœํ•˜์˜€์Šต๋‹ˆ๋‹ค.: "+email);
memberService.logout(email);
memberService.logout(email, accessToken);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.SafeNet.Backend.domain.member.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class JwtBlacklistService {
private RedisTemplate<String, String> redisTemplate;

public JwtBlacklistService(@Qualifier("tokenRedisTemplate") RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}

public void addToBlacklist(String token, long expirationTime) {
redisTemplate.opsForValue().set(token, "logout", expirationTime, TimeUnit.MILLISECONDS);
}

public boolean isBlacklisted(String token) {
return redisTemplate.hasKey(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ public TokenResponseDto login(LoginRequestDto loginRequestDto) throws Exception
/*
** ๋กœ๊ทธ์•„์›ƒ
*/
public void logout(String email) {
public void logout(String email, String atk) {
//Token์—์„œ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด getํ•ด ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ
try {
jwtTokenProvider.logout("JWT_TOKEN:" + email); //Token ์‚ญ์ œ
jwtTokenProvider.logout(email, atk); //Token ์‚ญ์ œ
}catch (CustomException ex) {
throw new CustomException("์ด๋ฏธ ๋กœ๊ทธ์•„์›ƒ๋œ ์œ ์ €์ž…๋‹ˆ๋‹ค");
}
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/com/SafeNet/Backend/global/auth/JwtFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.security.core.Authentication;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

Expand Down Expand Up @@ -44,23 +45,24 @@ protected void doFilterInternal(
// 2. validateToken ์œผ๋กœ ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
if (token != null && jwtTokenProvider.validateToken(token)) {
log.info("dofilterInternal : [ํ† ํฐ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ†ต๊ณผ.]");
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth); // ์ •์ƒ ํ† ํฐ์ด๋ฉด SecurityContext์— ์ €์žฅ
//Redis ์— ํ•ด๋‹น accessToken logout ์—ฌ๋ถ€ ํ™•์ธ : Logout์ด๋ฉด Redis์— ํŠน์ •๊ฐ’์ด ์ €์žฅ๋˜์–ด์žˆ์Œ
String isLogout = (String)redisTemplate.opsForValue().get(token);
if (ObjectUtils.isEmpty(isLogout)) { //๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ๋ผ๋ฉด,
if (ObjectUtils.isEmpty(isLogout)) { // ๋กœ๊ทธ์•„์›ƒ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด
// ํ† ํฐ์ด ์œ ํšจํ•  ๊ฒฝ์šฐ ํ† ํฐ์—์„œ Authentication ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์™€์„œ SecurityContext ์— ์ €์žฅ
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}else {
log.info("์œ ํšจํ•œ JWT ํ† ํฐ์ด ์—†์Šต๋‹ˆ๋‹ค. (๋กœ๊ทธ์•„์›ƒ๋œ ํ† ํฐ) ");
throw new AuthenticationException("๋กœ๊ทธ์•„์›ƒ๋œ ํ† ํฐ์ž…๋‹ˆ๋‹ค.") {};
}
}
} catch (RedisConnectionFailureException e) {
SecurityContextHolder.clearContext();
writeErrorResponse(response, HttpStatus.INTERNAL_SERVER_ERROR, "REDIS_ERROR", e);
} catch (Exception e) {
writeErrorResponse(response, HttpStatus.BAD_REQUEST, "INVALID_JWT", e);
} catch (AuthenticationException e) {
SecurityContextHolder.clearContext();
writeErrorResponse(response, HttpStatus.UNAUTHORIZED, "๋กœ๊ทธ์•„์›ƒ๋œ ๊ณ„์ •์ž…๋‹ˆ๋‹ค.", e);
}

try {
filterChain.doFilter(request, response);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.SafeNet.Backend.global.auth;

import ch.qos.logback.core.status.Status;
import com.SafeNet.Backend.domain.member.dto.TokenResponseDto;
import com.SafeNet.Backend.domain.member.service.UserDetailsServiceImpl;
import com.SafeNet.Backend.global.exception.CustomException;
Expand All @@ -9,19 +10,22 @@
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import jdk.jshell.Snippet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;


import java.security.Key;
import java.time.Duration;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Component
Expand Down Expand Up @@ -142,8 +146,9 @@ public boolean validateToken(String token){
throw new CustomException("INVALID_JWT");
}
}

// ๋ชจ๋“  ํ† ํฐ ํ—ค๋” ์„ค์ •
/*
* ๋ชจ๋“  ํ† ํฐ ํ—ค๋” ์„ค์ •
*/
public void setHeaderToken(HttpServletResponse response, TokenResponseDto dto) {
response.setHeader("Access_Token", dto.getAccessToken());
response.setHeader("Refresh_Token", dto.getRefreshToken());
Expand All @@ -152,9 +157,30 @@ public void setHeaderToken(HttpServletResponse response, TokenResponseDto dto) {
/*
* ๋กœ๊ทธ์•„์›ƒ ๋กœ์ง - Refresh ํ† ํฐ์„ redis์—์„œ ์‚ญ์ œ
*/
public void logout(String email) {
if(tokenRedisTemplate.opsForValue().get("JWT_TOKEN:" + email) != null) {
public ResponseEntity<Status> logout(String email, String token) {
String atk= token.substring(7);
Long expiration = getExpiration(atk);
if(tokenRedisTemplate.opsForValue().get(email) != null) {
tokenRedisTemplate.delete(email);
}
// [ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ ๋‹จ๊ณ„ ] redis์— ๊ฐ€์ ธ์˜จ key(JWT ํ† ํฐ) : value("logout")์œผ๋กœ ์ €์žฅ
tokenRedisTemplate.opsForValue().set(atk, "logout", Duration.ofMillis(expiration));

return new ResponseEntity<>(HttpStatus.OK);
}
/*
* ์œ ํšจ๊ธฐ๊ฐ„ ๊ฐ€์ ธ์˜ค๊ธฐ
*/
public Long getExpiration(String accessToken) {
// accessToken ๋‚จ์€ ์œ ํšจ์‹œ๊ฐ„
Date expiration = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(accessToken)
.getBody()
.getExpiration();
// ํ˜„์žฌ ์‹œ๊ฐ„
Long now = new Date().getTime();
return (expiration.getTime() - now);
}
}
Loading

0 comments on commit ddc4036

Please sign in to comment.