Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/main/java/Gotcha/common/config/SecurityFilterConfig.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package Gotcha.common.config;

import Gotcha.common.jwt.BlackListTokenService;
import Gotcha.common.jwt.TokenProvider;
import Gotcha.common.jwt.token.BlackListTokenService;
import Gotcha.common.jwt.auth.GuestDetailsService;
import Gotcha.common.jwt.token.TokenProvider;
import Gotcha.common.jwt.auth.UserDetailsServiceImpl;
import Gotcha.common.jwt.filter.JwtAuthenticationFilter;
import Gotcha.common.jwt.filter.JwtExceptionFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
@RequiredArgsConstructor
public class SecurityFilterConfig {
private final UserDetailsService userDetailsService;
private final UserDetailsServiceImpl userDetailsService;
private final GuestDetailsService guestDetailsService;
private final TokenProvider tokenProvider;
private final ObjectMapper objectMapper;
private final BlackListTokenService blackListTokenService;

@Bean
public JwtAuthenticationFilter authenticationFilter() {
return new JwtAuthenticationFilter(userDetailsService, tokenProvider, blackListTokenService);
return new JwtAuthenticationFilter(userDetailsService, guestDetailsService, tokenProvider, blackListTokenService);
}

@Bean
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/Gotcha/common/jwt/auth/GuestDetailsService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package Gotcha.common.jwt.auth;

import Gotcha.common.util.RedisUtil;
import Gotcha.domain.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Optional;

import static Gotcha.common.redis.RedisProperties.GUEST_KEY_PREFIX;
import static Gotcha.common.redis.RedisProperties.GUEST_TTL_SECONDS;

@Service
@RequiredArgsConstructor
public class GuestDetailsService implements UserDetailsService {
private final RedisUtil redisUtil;

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
Long guestId;
try {
guestId = Long.parseLong(username);
} catch (NumberFormatException e) {
throw new UsernameNotFoundException("Invalid guest ID: " + username);
}

User guest = Optional.ofNullable((User) redisUtil.getData(GUEST_KEY_PREFIX + guestId))
.orElseThrow(()-> new UsernameNotFoundException("Guest not found : " + guestId));

redisUtil.setDataExpire(GUEST_KEY_PREFIX + guestId, GUEST_TTL_SECONDS);

return new SecurityUserDetails(guest);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.auth;

import Gotcha.common.security.CustomGrantedAuthority;
import Gotcha.domain.user.entity.User;
Expand Down Expand Up @@ -38,6 +38,10 @@ public String getUsername() {
return user.getEmail();
}

public String getNickname(){
return user.getNickname();
}

public Long getId(){
return user.getId();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.auth;

import Gotcha.domain.user.entity.User;
import Gotcha.domain.user.repository.UserRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package Gotcha.common.jwt.filter;

import Gotcha.common.constants.SecurityConstants;
import Gotcha.common.jwt.BlackListTokenService;
import Gotcha.common.jwt.token.BlackListTokenService;
import Gotcha.common.jwt.exception.JwtExceptionCode;
import Gotcha.common.jwt.TokenProvider;
import Gotcha.common.jwt.token.TokenProvider;
import Gotcha.domain.user.entity.Role;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand All @@ -23,19 +24,33 @@
import java.io.IOException;
import java.util.Arrays;

import static Gotcha.common.jwt.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.JwtProperties.TOKEN_PREFIX;
import static Gotcha.common.jwt.token.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.TOKEN_PREFIX;

@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final UserDetailsService userDetailsService;
private final UserDetailsService guestDetailsService;
private final TokenProvider tokenProvider;
private final BlackListTokenService blackListTokenService;

private static final String SPECIAL_CHARACTERS_PATTERN = "[`':;|~!@#$%()^&*+=?/{}\\[\\]\\\"\\\\\"]+$";
private final AntPathMatcher antPathMatcher = new AntPathMatcher();

public JwtAuthenticationFilter(
@Qualifier("userDetailsServiceImpl") UserDetailsService userDetailsService,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔한 빈 관리 좋네요!!

@Qualifier("guestDetailsService") UserDetailsService guestDetailsService,
TokenProvider tokenProvider,
BlackListTokenService blackListTokenService
) {
this.userDetailsService = userDetailsService;
this.guestDetailsService = guestDetailsService;
this.tokenProvider = tokenProvider;
this.blackListTokenService = blackListTokenService;
}




@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Expand All @@ -52,8 +67,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse

String accessToken = resolveAccessToken(response, accessTokenHeader);

String username = tokenProvider.getEmail(accessToken);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
String role = tokenProvider.getRole(accessToken);
UserDetails userDetails;
if (role.equals(String.valueOf(Role.GUEST))) {
Long guestId = tokenProvider.getUserId(accessToken);
userDetails = guestDetailsService.loadUserByUsername(guestId.toString());
}
else{
String username = tokenProvider.getUsername(accessToken);
userDetails = userDetailsService.loadUserByUsername(username);
}

Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.token;

import Gotcha.common.util.RedisUtil;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.token;

import Gotcha.common.exception.CustomException;
import Gotcha.common.jwt.exception.JwtExceptionCode;
Expand All @@ -11,8 +11,8 @@

import java.time.LocalDateTime;

import static Gotcha.common.jwt.JwtProperties.REFRESH_COOKIE_VALUE;
import static Gotcha.common.jwt.JwtProperties.TOKEN_PREFIX;
import static Gotcha.common.jwt.token.JwtProperties.REFRESH_COOKIE_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.TOKEN_PREFIX;

@RequiredArgsConstructor
@Component
Expand All @@ -26,31 +26,41 @@ public class JwtHelper {
public TokenDto createToken(User user) {
Long userId = user.getId();
String email = user.getEmail();
return getTokenDto(user, userId, email);
}

public TokenDto createGuestToken(User guest){
Long userId = guest.getId();
String username = guest.getNickname();
return getTokenDto(guest, userId, username);
}

private TokenDto getTokenDto(User user, Long userId, String username) {
String role = String.valueOf(user.getRole());

String accessToken = TOKEN_PREFIX + tokenProvider.createAccessToken(role, userId, email);
String refreshToken = tokenProvider.createRefreshToken(role, userId, email);
String accessToken = TOKEN_PREFIX + tokenProvider.createAccessToken(role, userId, username);
String refreshToken = tokenProvider.createRefreshToken(role, userId, username);

refreshTokenService.saveRefreshToken(email, refreshToken);
refreshTokenService.saveRefreshToken(username, refreshToken);
return new TokenDto(accessToken, refreshToken);
}

public TokenDto reissueToken(String refreshToken) {
refreshTokenService.isExpiredRefreshToken(refreshToken);

String email = tokenProvider.getEmail(refreshToken);
String username = tokenProvider.getUsername(refreshToken);

if (!refreshTokenService.existedRefreshToken(email, refreshToken))
if (!refreshTokenService.existedRefreshToken(username, refreshToken))
throw new CustomException(JwtExceptionCode.REFRESH_TOKEN_NOT_FOUND);

Long userId = tokenProvider.getUserId(refreshToken);
String role = tokenProvider.getRole(refreshToken);

String newAccessToken = TOKEN_PREFIX + tokenProvider.createAccessToken(role, userId, email);
String newRefreshToken = tokenProvider.createRefreshToken(role, userId, email);
String newAccessToken = TOKEN_PREFIX + tokenProvider.createAccessToken(role, userId, username);
String newRefreshToken = tokenProvider.createRefreshToken(role, userId, username);

refreshTokenService.deleteRefreshToken(refreshToken);
refreshTokenService.saveRefreshToken(email, newRefreshToken);
refreshTokenService.saveRefreshToken(username, newRefreshToken);

return new TokenDto(newAccessToken, newRefreshToken);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.token;

public interface JwtProperties {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.token;

import Gotcha.common.exception.CustomException;
import Gotcha.common.jwt.exception.JwtExceptionCode;
Expand All @@ -19,20 +19,20 @@ public class RefreshTokenService {
@Value("${token.refresh.in-redis}")
private long REFRESH_EXPIRATION;

public void saveRefreshToken(String email, String refreshToken) {
String key = REFRESH_TOKEN_KEY_PREFIX + email;
public void saveRefreshToken(String username, String refreshToken) {
String key = REFRESH_TOKEN_KEY_PREFIX + username;
redisUtil.setData(key, refreshToken);
redisUtil.setDataExpire(key, REFRESH_EXPIRATION);
}

public void deleteRefreshToken(String refreshToken) {
String email = tokenProvider.getEmail(refreshToken);
String key = REFRESH_TOKEN_KEY_PREFIX + email;
String username = tokenProvider.getUsername(refreshToken);
String key = REFRESH_TOKEN_KEY_PREFIX + username;
redisUtil.deleteData(key);
}

public boolean existedRefreshToken(String email, String requestRefreshToken) {
String key = REFRESH_TOKEN_KEY_PREFIX + email;
public boolean existedRefreshToken(String username, String requestRefreshToken) {
String key = REFRESH_TOKEN_KEY_PREFIX + username;

String storedRefreshToken = (String) redisUtil.getData(key);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gotcha.common.jwt;
package Gotcha.common.jwt.token;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
Expand Down Expand Up @@ -68,7 +68,7 @@ private Claims getClaims(String token) {
.getPayload();
}

public String getEmail(String token) {
public String getUsername(String token) {
return getClaims(token).getSubject();
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/Gotcha/common/redis/RedisProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ public interface RedisProperties {
String CODE_KEY_PREFIX = "code::";
String EMAIL_VERIFY_KEY_PREFIX = "emailVerify::";
String NICKNAME_VERIFY_KEY_PREFIX = "nicknameVerify::";
String GUEST_KEY_PREFIX = "guest::";

long CODE_EXPIRATION_TIME = 5*60;

long GUEST_TTL_SECONDS = 30 * 60;
}
2 changes: 1 addition & 1 deletion src/main/java/Gotcha/common/util/RedisUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public Object getData(String key) {
return redisTemplate.opsForValue().get(key);
}

public void setData(String key, String value) {
public void setData(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/Gotcha/domain/auth/api/AuthApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

import static Gotcha.common.jwt.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.JwtProperties.REFRESH_COOKIE_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.REFRESH_COOKIE_VALUE;

@Tag(name = "[인증 API]", description = "인증 관련 API")
public interface AuthApi {
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/Gotcha/domain/auth/controller/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
import java.util.HashMap;
import java.util.Map;

import static Gotcha.common.jwt.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.JwtProperties.REFRESH_COOKIE_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.ACCESS_HEADER_VALUE;
import static Gotcha.common.jwt.token.JwtProperties.REFRESH_COOKIE_VALUE;

@RestController
@RequiredArgsConstructor
Expand All @@ -55,6 +55,13 @@ public ResponseEntity<?> signIn(@Valid @RequestBody SignInReq signInReq) {
return createTokenRes(tokenDto);
}

@PostMapping("/guest/sign-in")
public ResponseEntity<?> guestSignIn(){
TokenDto tokenDto = authService.guestSignIn();

return createTokenRes(tokenDto);
}

@PostMapping("/token-reissue")
public ResponseEntity<?> reIssueToken(@CookieValue(name = REFRESH_COOKIE_VALUE, required = false) String refreshToken) {
if (refreshToken == null) {
Expand Down
Loading