From 4a013f1377cf9995e8bc54bc938c79de0021b033 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 00:32:42 +0900 Subject: [PATCH 01/10] =?UTF-8?q?chore:=20Redis=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/core/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/core/build.gradle b/backend/core/build.gradle index f7009086..71dc2d2f 100644 --- a/backend/core/build.gradle +++ b/backend/core/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation 'org.mapstruct:mapstruct:1.5.5.Final' implementation 'org.jsoup:jsoup:1.15.3' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' From d40339c37425f0225ac0cb5483b874fe35523e28 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:42:59 +0900 Subject: [PATCH 02/10] =?UTF-8?q?delete:=20=EC=9E=90=EC=B2=B4=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/handler/LoginFailureHandler.java | 22 --------- .../login/handler/LoginSuccessHandler.java | 48 ------------------- .../global/login/service/LoginService.java | 29 ----------- 3 files changed, 99 deletions(-) delete mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginFailureHandler.java delete mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginSuccessHandler.java delete mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/login/service/LoginService.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginFailureHandler.java b/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginFailureHandler.java deleted file mode 100644 index 80170d03..00000000 --- a/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginFailureHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.rollthedice.backend.global.login.handler; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; - -import java.io.IOException; - -@Slf4j -public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - response.setCharacterEncoding("UTF-8"); - response.setContentType("text/plain;charset=UTF-8"); - response.getWriter().write("로그인 실패. 이메일이나 비밀번호를 확인해주세요."); - log.info("로그인에 실패했습니다. 메시지 : {}", exception.getMessage()); - } -} diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginSuccessHandler.java b/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginSuccessHandler.java deleted file mode 100644 index f68e5c3a..00000000 --- a/backend/core/src/main/java/com/rollthedice/backend/global/login/handler/LoginSuccessHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.rollthedice.backend.global.login.handler; - -import com.rollthedice.backend.domain.member.repository.MemberRepository; -import com.rollthedice.backend.global.jwt.service.JwtService; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; - -@Slf4j -@RequiredArgsConstructor -public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - private final JwtService jwtService; - private final MemberRepository memberRepository; - - @Value("${jwt.access.expiration}") - private String accessTokenExpiration; - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) { - String email = extractUsername(authentication); - String accessToken = jwtService.createAccessToken(email); - String refreshToken = jwtService.createRefreshToken(); - - jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); - - memberRepository.findByEmail(email) - .ifPresent(user -> { - user.updateRefreshToken(refreshToken); - memberRepository.saveAndFlush(user); - }); - log.info("로그인에 성공하였습니다. 이메일 : {}", email); - log.info("로그인에 성공하였습니다. AccessToken : {}", accessToken); - log.info("발급된 AccessToken 만료 기간 : {}", accessTokenExpiration); - } - - private String extractUsername(Authentication authentication) { - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - return userDetails.getUsername(); - } -} - - diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/login/service/LoginService.java b/backend/core/src/main/java/com/rollthedice/backend/global/login/service/LoginService.java deleted file mode 100644 index eca621ca..00000000 --- a/backend/core/src/main/java/com/rollthedice/backend/global/login/service/LoginService.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.rollthedice.backend.global.login.service; - -import com.rollthedice.backend.domain.member.entity.Member; -import com.rollthedice.backend.domain.member.repository.MemberRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.userdetails.User; -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; - -@Service -@RequiredArgsConstructor -public class LoginService implements UserDetailsService { - private final MemberRepository memberRepository; - - @Override - public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { - Member member = memberRepository.findByEmail(email) - .orElseThrow(() -> new UsernameNotFoundException("해당 이메일이 존재하지 않습니다.")); - - return User.builder() - .username(member.getEmail()) - .password(member.getPassword()) - .roles(member.getRole().toString()) - .build(); - } -} - From 0fc7ca9a54c54169f0f063207342ab3669d73619 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:43:37 +0900 Subject: [PATCH 03/10] style: login.filter -> oauth2.filter --- .../filter/CustomJsonUsernamePasswordAuthenticationFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/core/src/main/java/com/rollthedice/backend/global/{login => oauth2}/filter/CustomJsonUsernamePasswordAuthenticationFilter.java (97%) diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/login/filter/CustomJsonUsernamePasswordAuthenticationFilter.java b/backend/core/src/main/java/com/rollthedice/backend/global/oauth2/filter/CustomJsonUsernamePasswordAuthenticationFilter.java similarity index 97% rename from backend/core/src/main/java/com/rollthedice/backend/global/login/filter/CustomJsonUsernamePasswordAuthenticationFilter.java rename to backend/core/src/main/java/com/rollthedice/backend/global/oauth2/filter/CustomJsonUsernamePasswordAuthenticationFilter.java index 3093a95a..165cda75 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/global/login/filter/CustomJsonUsernamePasswordAuthenticationFilter.java +++ b/backend/core/src/main/java/com/rollthedice/backend/global/oauth2/filter/CustomJsonUsernamePasswordAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.rollthedice.backend.global.login.filter; +package com.rollthedice.backend.global.oauth2.filter; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; From c25117dae9c13f53fd1cf3509f2c942c71c79a1b Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:44:34 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20Redis=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/RedisConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/config/RedisConfig.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/config/RedisConfig.java b/backend/core/src/main/java/com/rollthedice/backend/global/config/RedisConfig.java new file mode 100644 index 00000000..28a90cb8 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/config/RedisConfig.java @@ -0,0 +1,21 @@ +package com.rollthedice.backend.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; + +@Configuration +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } +} \ No newline at end of file From a5b18155660a0da6e4d4e6b88c2484213521e48c Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:45:54 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20=EA=B0=9C=EB=B3=84=20RefreshT?= =?UTF-8?q?oken=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/member/entity/Member.java | 8 +---- .../member/repository/MemberRepository.java | 2 -- .../jwt/refresh/domain/RefreshToken.java | 34 +++++++++++++++++++ .../repository/RefreshTokenRepository.java | 10 ++++++ 4 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/domain/RefreshToken.java create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/repository/RefreshTokenRepository.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/member/entity/Member.java b/backend/core/src/main/java/com/rollthedice/backend/domain/member/entity/Member.java index d75aa018..81c0d3e1 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/member/entity/Member.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/member/entity/Member.java @@ -14,14 +14,12 @@ public class Member extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String oauthId; //로그인한 소셜 타입의 식별자 값 + private String oauthId; private String nickname; private String email; private String password; private String imageUrl; - private String refreshToken; - @Enumerated(EnumType.STRING) private Role role; @@ -37,10 +35,6 @@ public Member update(String email, String imageUrl) { return this; } - public void updateRefreshToken(String updateRefreshToken) { - this.refreshToken = updateRefreshToken; - } - public void signUp(String nickname) { this.nickname = nickname; this.role = Role.USER; diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/member/repository/MemberRepository.java b/backend/core/src/main/java/com/rollthedice/backend/domain/member/repository/MemberRepository.java index bb8615f7..0f504f18 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/member/repository/MemberRepository.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/member/repository/MemberRepository.java @@ -11,7 +11,5 @@ public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); - Optional findByRefreshToken(String refreshToken); - Optional findBySocialTypeAndOauthId(SocialType socialType, String oauthId); } diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/domain/RefreshToken.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/domain/RefreshToken.java new file mode 100644 index 00000000..bd242ba4 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/domain/RefreshToken.java @@ -0,0 +1,34 @@ +package com.rollthedice.backend.global.jwt.refresh.domain; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; +import org.springframework.data.redis.core.index.Indexed; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@RedisHash(value = "refreshToken") +public class RefreshToken { + + @Id + private String email; + + @Indexed + private String refreshToken; + + @TimeToLive() + private Long expirationPeriod; + + public RefreshToken(String email) { + this.email = email; + } + + public void createRefreshToken(String refreshToken, Long expiration) { + this.refreshToken = refreshToken; + this.expirationPeriod = expiration; + } +} + diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/repository/RefreshTokenRepository.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/repository/RefreshTokenRepository.java new file mode 100644 index 00000000..7055e936 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/repository/RefreshTokenRepository.java @@ -0,0 +1,10 @@ +package com.rollthedice.backend.global.jwt.refresh.repository; + +import com.rollthedice.backend.global.jwt.refresh.domain.RefreshToken; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends CrudRepository { + Optional findByRefreshToken(String refreshToken); +} From b5035ce2817bed1398ea3cd6b9121c5cabbe6c4b Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:46:43 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20RefreshTokenService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../refresh/service/RefreshTokenService.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/service/RefreshTokenService.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/service/RefreshTokenService.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/service/RefreshTokenService.java new file mode 100644 index 00000000..b9b878fb --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/refresh/service/RefreshTokenService.java @@ -0,0 +1,34 @@ +package com.rollthedice.backend.global.jwt.refresh.service; + +import com.rollthedice.backend.global.jwt.exception.NotFoundTokenException; +import com.rollthedice.backend.global.jwt.refresh.domain.RefreshToken; +import com.rollthedice.backend.global.jwt.refresh.repository.RefreshTokenRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class RefreshTokenService { + private final RefreshTokenRepository refreshTokenRepository; + + @Value("${jwt.refresh.expiration}") + private Long expirationPeriod; + + public void updateToken(String email, String token) { + RefreshToken refreshToken = refreshTokenRepository.findById(email).orElse(new RefreshToken(email)); + refreshToken.createRefreshToken(token, expirationPeriod / 1000); + log.info("토큰 값 : " + refreshToken.getRefreshToken()); + log.info("이메일 : " + refreshToken.getEmail()); + log.info("유효기간 : " + refreshToken.getExpirationPeriod()); + refreshTokenRepository.save(refreshToken); + } + + public RefreshToken findByToken(String token) { + return refreshTokenRepository.findByRefreshToken(token) + .orElseThrow(NotFoundTokenException::new); + } + +} From bed4254088712aa87d2eb819c20c9f3b301690c9 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:47:15 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20NotFoundTokenException=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwt/exception/NotFoundTokenException.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/jwt/exception/NotFoundTokenException.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/exception/NotFoundTokenException.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/exception/NotFoundTokenException.java new file mode 100644 index 00000000..658192ee --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/exception/NotFoundTokenException.java @@ -0,0 +1,14 @@ +package com.rollthedice.backend.global.jwt.exception; + +public class NotFoundTokenException extends RuntimeException { + public NotFoundTokenException() { + } + + public NotFoundTokenException(String message) { + super(message); + } + + public NotFoundTokenException(String message, Throwable cause) { + super(message, cause); + } +} From d4bd456bd46b1543d384e2e3e6c253b3c2808e60 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:48:41 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20redis=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20refreshToken=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/SecurityConfig.java | 49 +++---------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/config/SecurityConfig.java b/backend/core/src/main/java/com/rollthedice/backend/global/config/SecurityConfig.java index c006cc78..770cf1bb 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/global/config/SecurityConfig.java +++ b/backend/core/src/main/java/com/rollthedice/backend/global/config/SecurityConfig.java @@ -3,20 +3,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.rollthedice.backend.domain.member.repository.MemberRepository; import com.rollthedice.backend.global.jwt.filter.JwtAuthenticationProcessingFilter; +import com.rollthedice.backend.global.jwt.refresh.service.RefreshTokenService; import com.rollthedice.backend.global.jwt.service.JwtService; -import com.rollthedice.backend.global.login.filter.CustomJsonUsernamePasswordAuthenticationFilter; -import com.rollthedice.backend.global.login.handler.LoginFailureHandler; -import com.rollthedice.backend.global.login.handler.LoginSuccessHandler; -import com.rollthedice.backend.global.login.service.LoginService; +//import com.rollthedice.backend.global.login.handler.LoginFailureHandler; +//import com.rollthedice.backend.global.login.handler.LoginSuccessHandler; +//import com.rollthedice.backend.global.login.service.LoginService; import com.rollthedice.backend.global.oauth2.handler.OAuth2LoginFailureHandler; import com.rollthedice.backend.global.oauth2.handler.OAuth2LoginSuccessHandler; import com.rollthedice.backend.global.oauth2.service.CustomOAuth2UserService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -31,13 +28,13 @@ @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { - private final LoginService loginService; private final JwtService jwtService; private final MemberRepository memberRepository; - private final ObjectMapper objectMapper; private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; private final OAuth2LoginFailureHandler oAuth2LoginFailureHandler; private final CustomOAuth2UserService customOAuth2UserService; + private final RefreshTokenService refreshTokenService; + @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @@ -58,9 +55,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .failureHandler(oAuth2LoginFailureHandler) .userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) //customUserService 설정 ); - http.addFilterAfter(customJsonUsernamePasswordAuthenticationFilter(), LogoutFilter.class); - http.addFilterBefore(jwtAuthenticationProcessingFilter(), CustomJsonUsernamePasswordAuthenticationFilter.class); - + http.addFilterAfter(jwtAuthenticationProcessingFilter(), LogoutFilter.class); return http.build(); } @@ -69,36 +64,8 @@ public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } - @Bean - public AuthenticationManager authenticationManager() { - DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); - provider.setPasswordEncoder(passwordEncoder()); - provider.setUserDetailsService(loginService); - return new ProviderManager(provider); - } - - @Bean - public LoginSuccessHandler loginSuccessHandler() { - return new LoginSuccessHandler(jwtService, memberRepository); - } - - @Bean - public LoginFailureHandler loginFailureHandler() { - return new LoginFailureHandler(); - } - - @Bean - public CustomJsonUsernamePasswordAuthenticationFilter customJsonUsernamePasswordAuthenticationFilter() { - CustomJsonUsernamePasswordAuthenticationFilter customJsonUsernamePasswordLoginFilter - = new CustomJsonUsernamePasswordAuthenticationFilter(objectMapper); - customJsonUsernamePasswordLoginFilter.setAuthenticationManager(authenticationManager()); - customJsonUsernamePasswordLoginFilter.setAuthenticationSuccessHandler(loginSuccessHandler()); - customJsonUsernamePasswordLoginFilter.setAuthenticationFailureHandler(loginFailureHandler()); - return customJsonUsernamePasswordLoginFilter; - } - @Bean public JwtAuthenticationProcessingFilter jwtAuthenticationProcessingFilter() { - return new JwtAuthenticationProcessingFilter(jwtService, memberRepository); + return new JwtAuthenticationProcessingFilter(jwtService, refreshTokenService, memberRepository); } } From 5e467b7f8ec5b8475f8651099c5e28f66ed89221 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:49:22 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20ExceptionAdvice=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/advice/ExceptionAdvice.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/core/src/main/java/com/rollthedice/backend/global/advice/ExceptionAdvice.java diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/advice/ExceptionAdvice.java b/backend/core/src/main/java/com/rollthedice/backend/global/advice/ExceptionAdvice.java new file mode 100644 index 00000000..9ae7bf53 --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/advice/ExceptionAdvice.java @@ -0,0 +1,16 @@ +package com.rollthedice.backend.global.advice; + +import com.rollthedice.backend.global.jwt.exception.NotFoundTokenException; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ExceptionAdvice { + @ExceptionHandler(NotFoundTokenException.class) + public ResponseEntity notFoundTokenException() { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } +} From 73bcf2bd97d570900db184beba6b14dd8766ba11 Mon Sep 17 00:00:00 2001 From: yeonjy Date: Tue, 26 Mar 2024 04:54:36 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20redis=20-=20refresh=20token?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 12 +++++++ .../JwtAuthenticationProcessingFilter.java | 33 ++++++++++--------- .../global/jwt/service/JwtService.java | 12 ++++--- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/backend/core/src/main/java/com/rollthedice/backend/domain/member/service/MemberService.java b/backend/core/src/main/java/com/rollthedice/backend/domain/member/service/MemberService.java index 07f36173..ef8a80fc 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/domain/member/service/MemberService.java +++ b/backend/core/src/main/java/com/rollthedice/backend/domain/member/service/MemberService.java @@ -3,6 +3,10 @@ import com.rollthedice.backend.domain.member.dto.SignUpDto; import com.rollthedice.backend.domain.member.entity.Member; import com.rollthedice.backend.domain.member.query.AuthService; +import com.rollthedice.backend.global.jwt.refresh.service.RefreshTokenService; +import com.rollthedice.backend.global.jwt.service.JwtService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -11,10 +15,18 @@ @Service public class MemberService { private final AuthService authService; + private final RefreshTokenService refreshTokenService; + private final JwtService jwtService; + private final HttpServletRequest request; + private final HttpServletResponse response; @Transactional public void signUp(SignUpDto dto) { Member member = authService.getMember(); member.signUp(dto.getNickname()); + + String refreshToken = jwtService.createRefreshToken(); + jwtService.setRefreshTokenHeader(response, refreshToken); + refreshTokenService.updateToken(member.getEmail(), refreshToken); } } diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/filter/JwtAuthenticationProcessingFilter.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/filter/JwtAuthenticationProcessingFilter.java index 690fdbb4..e47cd54a 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/filter/JwtAuthenticationProcessingFilter.java +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/filter/JwtAuthenticationProcessingFilter.java @@ -2,6 +2,8 @@ import com.rollthedice.backend.domain.member.entity.Member; import com.rollthedice.backend.domain.member.repository.MemberRepository; +import com.rollthedice.backend.global.jwt.refresh.domain.RefreshToken; +import com.rollthedice.backend.global.jwt.refresh.service.RefreshTokenService; import com.rollthedice.backend.global.jwt.service.JwtService; import com.rollthedice.backend.global.jwt.util.PasswordUtil; import jakarta.servlet.FilterChain; @@ -27,14 +29,16 @@ public class JwtAuthenticationProcessingFilter extends OncePerRequestFilter { private static final String NO_CHECK_URL = "/login"; private final JwtService jwtService; + private final RefreshTokenService refreshTokenService; private final MemberRepository memberRepository; private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { if (request.getRequestURI().equals(NO_CHECK_URL)) { - filterChain.doFilter(request, response); // "/login" 요청이 들어오면, 다음 필터 호출 + filterChain.doFilter(request, response); return; } String refreshToken = jwtService.extractRefreshToken(request) @@ -46,29 +50,26 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse return; } - if (refreshToken == null) { - log.info("refresh token is null"); - checkAccessTokenAndAuthentication(request, response, filterChain); - } + log.info("refresh token is null"); + checkAccessTokenAndAuthentication(request, response, filterChain); } public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) { - memberRepository.findByRefreshToken(refreshToken) - .ifPresent(member -> { - String reIssueRefreshToken = reIssueRefreshToken(member); - jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(member.getEmail()), - reIssueRefreshToken); - }); + RefreshToken refresh = refreshTokenService.findByToken(refreshToken); + String reIssuedRefreshToken = reIssueRefreshToken(refresh.getEmail()); + jwtService.sendAccessAndRefreshToken(response, + jwtService.createAccessToken(refresh.getEmail()), reIssuedRefreshToken); } - private String reIssueRefreshToken(Member member) { + private String reIssueRefreshToken(String email) { String reIssuedRefreshToken = jwtService.createRefreshToken(); - member.updateRefreshToken(reIssuedRefreshToken); - memberRepository.saveAndFlush(member); + + refreshTokenService.updateToken(email, reIssuedRefreshToken); return reIssuedRefreshToken; } - private void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + private void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { jwtService.extractAccessToken(request) .filter(jwtService::isTokenValid) .ifPresent(accessToken -> jwtService.extractEmail(accessToken) diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/service/JwtService.java b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/service/JwtService.java index fd5b2230..0332e118 100644 --- a/backend/core/src/main/java/com/rollthedice/backend/global/jwt/service/JwtService.java +++ b/backend/core/src/main/java/com/rollthedice/backend/global/jwt/service/JwtService.java @@ -3,6 +3,7 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.rollthedice.backend.domain.member.repository.MemberRepository; +import com.rollthedice.backend.global.jwt.refresh.service.RefreshTokenService; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Getter; @@ -42,6 +43,7 @@ public class JwtService { private static final String BEARER = "Bearer "; private final MemberRepository memberRepository; + private final RefreshTokenService refreshTokenService; public String createAccessToken(String email) { Date now = new Date(); @@ -98,11 +100,7 @@ public Optional extractEmail(String accessToken) { @Transactional public void updateRefreshToken(String email, String refreshToken) { - memberRepository.findByEmail(email) - .ifPresentOrElse( - member -> member.updateRefreshToken(refreshToken), - () -> new Exception("일치하는 회원이 없습니다.") - ); + refreshTokenService.updateToken(email, refreshToken); } public boolean isTokenValid(String token) { @@ -114,6 +112,10 @@ public boolean isTokenValid(String token) { return false; } } + + public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) { + response.setHeader(refreshHeader, BEARER + refreshToken); + } }