From f2410adc20a38f5d5a66c3e01dfe867888950d1a Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:11:15 +0900 Subject: [PATCH 1/9] fix : update successHandler logic --- .../oauth/handler/OAuthSuccessHandler.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/reddiserver/security/oauth/handler/OAuthSuccessHandler.java b/src/main/java/com/example/reddiserver/security/oauth/handler/OAuthSuccessHandler.java index 74b4e5a..f3bf65d 100644 --- a/src/main/java/com/example/reddiserver/security/oauth/handler/OAuthSuccessHandler.java +++ b/src/main/java/com/example/reddiserver/security/oauth/handler/OAuthSuccessHandler.java @@ -1,16 +1,14 @@ package com.example.reddiserver.security.oauth.handler; -import com.example.reddiserver.dto.security.TokenDto; import com.example.reddiserver.security.jwt.TokenProvider; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; -import org.springframework.web.util.UriComponentsBuilder; import java.io.IOException; @@ -19,15 +17,17 @@ public class OAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final TokenProvider tokenProvider; + private static final ObjectMapper mapper = new ObjectMapper(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - TokenDto tokenDto = tokenProvider.createAccessToken(authentication); + // Access, Refresh Token Body 저장 + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); - String targetUrl = UriComponentsBuilder.fromUriString(getDefaultTargetUrl()) - .queryParam("token", tokenDto) - .build().toUriString(); + String tokenDto = mapper.writeValueAsString(tokenProvider.createAccessToken(authentication)); + response.getWriter().write(tokenDto); - getRedirectStrategy().sendRedirect(request, response, targetUrl); + response.getWriter().flush(); } } From d76629084f9c9c4a7e9f572e53deb7637a854696 Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:50:20 +0900 Subject: [PATCH 2/9] =?UTF-8?q?refactor=20:=20Jwt=20Dto=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=ED=8F=B4=EB=8D=94=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/security/{ => response}/JwtErrorResponse.java | 2 +- .../reddiserver/dto/security/{ => response}/TokenDto.java | 2 +- .../reddiserver/security/jwt/JwtExceptionHandlerFilter.java | 2 +- .../com/example/reddiserver/security/jwt/TokenProvider.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/example/reddiserver/dto/security/{ => response}/JwtErrorResponse.java (93%) rename src/main/java/com/example/reddiserver/dto/security/{ => response}/TokenDto.java (73%) diff --git a/src/main/java/com/example/reddiserver/dto/security/JwtErrorResponse.java b/src/main/java/com/example/reddiserver/dto/security/response/JwtErrorResponse.java similarity index 93% rename from src/main/java/com/example/reddiserver/dto/security/JwtErrorResponse.java rename to src/main/java/com/example/reddiserver/dto/security/response/JwtErrorResponse.java index ab326c8..89b7570 100644 --- a/src/main/java/com/example/reddiserver/dto/security/JwtErrorResponse.java +++ b/src/main/java/com/example/reddiserver/dto/security/response/JwtErrorResponse.java @@ -1,4 +1,4 @@ -package com.example.reddiserver.dto.security; +package com.example.reddiserver.dto.security.response; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/main/java/com/example/reddiserver/dto/security/TokenDto.java b/src/main/java/com/example/reddiserver/dto/security/response/TokenDto.java similarity index 73% rename from src/main/java/com/example/reddiserver/dto/security/TokenDto.java rename to src/main/java/com/example/reddiserver/dto/security/response/TokenDto.java index 61fd7fb..10038b5 100644 --- a/src/main/java/com/example/reddiserver/dto/security/TokenDto.java +++ b/src/main/java/com/example/reddiserver/dto/security/response/TokenDto.java @@ -1,4 +1,4 @@ -package com.example.reddiserver.dto.security; +package com.example.reddiserver.dto.security.response; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/com/example/reddiserver/security/jwt/JwtExceptionHandlerFilter.java b/src/main/java/com/example/reddiserver/security/jwt/JwtExceptionHandlerFilter.java index d743a61..ba11a09 100644 --- a/src/main/java/com/example/reddiserver/security/jwt/JwtExceptionHandlerFilter.java +++ b/src/main/java/com/example/reddiserver/security/jwt/JwtExceptionHandlerFilter.java @@ -1,6 +1,6 @@ package com.example.reddiserver.security.jwt; -import com.example.reddiserver.dto.security.JwtErrorResponse; +import com.example.reddiserver.dto.security.response.JwtErrorResponse; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.MalformedJwtException; diff --git a/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java b/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java index 610c546..82b3a12 100644 --- a/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java +++ b/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java @@ -1,6 +1,6 @@ package com.example.reddiserver.security.jwt; -import com.example.reddiserver.dto.security.TokenDto; +import com.example.reddiserver.dto.security.response.TokenDto; import com.example.reddiserver.entity.RefreshToken; import com.example.reddiserver.repository.RefreshTokenRepository; import io.jsonwebtoken.*; From 5a02e26c2b0575d1403875b3d9f079c28c114a36 Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:50:57 +0900 Subject: [PATCH 3/9] feat : add ApiResponse success message Response --- src/main/java/com/example/reddiserver/common/ApiResponse.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/example/reddiserver/common/ApiResponse.java b/src/main/java/com/example/reddiserver/common/ApiResponse.java index d994f78..be02ccf 100644 --- a/src/main/java/com/example/reddiserver/common/ApiResponse.java +++ b/src/main/java/com/example/reddiserver/common/ApiResponse.java @@ -18,6 +18,10 @@ public static ApiResponse successResponse(T data) { return new ApiResponse<>(SUCCESS_STATUS, data, null); } + public static ApiResponse successResponse(T data, String message) { + return new ApiResponse<>(SUCCESS_STATUS, data, message); + } + public static ApiResponse successWithNoContent() { return new ApiResponse<>(SUCCESS_STATUS, null, null); } From bd7ccdb30cda7420fd1539f131269f36aea7f6bd Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:51:12 +0900 Subject: [PATCH 4/9] feat : create reissueRequestDto --- .../reddiserver/dto/auth/request/ReissueRequestDto.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/example/reddiserver/dto/auth/request/ReissueRequestDto.java diff --git a/src/main/java/com/example/reddiserver/dto/auth/request/ReissueRequestDto.java b/src/main/java/com/example/reddiserver/dto/auth/request/ReissueRequestDto.java new file mode 100644 index 0000000..202a7b3 --- /dev/null +++ b/src/main/java/com/example/reddiserver/dto/auth/request/ReissueRequestDto.java @@ -0,0 +1,9 @@ +package com.example.reddiserver.dto.auth.request; + +import lombok.Getter; + +@Getter +public class ReissueRequestDto { + private String accessToken; + private String refreshToken; +} From 676460e5ef31a683ca738052866df3d3f24de0a5 Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:51:24 +0900 Subject: [PATCH 5/9] feat : create AuthController --- .../controller/auth/AuthController.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/example/reddiserver/controller/auth/AuthController.java diff --git a/src/main/java/com/example/reddiserver/controller/auth/AuthController.java b/src/main/java/com/example/reddiserver/controller/auth/AuthController.java new file mode 100644 index 0000000..68848cc --- /dev/null +++ b/src/main/java/com/example/reddiserver/controller/auth/AuthController.java @@ -0,0 +1,23 @@ +package com.example.reddiserver.controller.auth; + +import com.example.reddiserver.common.ApiResponse; +import com.example.reddiserver.dto.auth.request.ReissueRequestDto; +import com.example.reddiserver.dto.security.response.TokenDto; +import com.example.reddiserver.service.auth.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/auth") +public class AuthController { + private final AuthService authService; + + @PostMapping("/reissue") + public ApiResponse reissue(@RequestBody ReissueRequestDto reissueRequestDto) { + return ApiResponse.successResponse(authService.reissue(reissueRequestDto), "Access Token 재발급 성공"); + } +} From 9aaae49e9f27307868e1c445aa43e116a249a18f Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:51:37 +0900 Subject: [PATCH 6/9] feat : create AuthService --- .../reddiserver/service/auth/AuthService.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/example/reddiserver/service/auth/AuthService.java diff --git a/src/main/java/com/example/reddiserver/service/auth/AuthService.java b/src/main/java/com/example/reddiserver/service/auth/AuthService.java new file mode 100644 index 0000000..2efddd2 --- /dev/null +++ b/src/main/java/com/example/reddiserver/service/auth/AuthService.java @@ -0,0 +1,27 @@ +package com.example.reddiserver.service.auth; + +import com.example.reddiserver.dto.auth.request.ReissueRequestDto; +import com.example.reddiserver.dto.security.response.TokenDto; +import com.example.reddiserver.security.jwt.TokenProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AuthService { + private final TokenProvider tokenProvider; + + @Transactional + public TokenDto reissue(ReissueRequestDto reissueRequestDto) { + if (!tokenProvider.refreshTokenValidation(reissueRequestDto.getRefreshToken())) { + throw new RuntimeException("유효하지 않은 Refresh Token 입니다."); + } + + Authentication authentication = tokenProvider.getAuthentication(reissueRequestDto.getAccessToken()); + + return tokenProvider.createAccessToken(authentication); + } +} From ca897af99fdbd61ef4ee1cca86c9431ca51e0c67 Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 06:58:45 +0900 Subject: [PATCH 7/9] =?UTF-8?q?fix=20:=20token=20=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reddiserver/security/oauth/PrincipalOAuth2Details.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/reddiserver/security/oauth/PrincipalOAuth2Details.java b/src/main/java/com/example/reddiserver/security/oauth/PrincipalOAuth2Details.java index 0015873..a781132 100644 --- a/src/main/java/com/example/reddiserver/security/oauth/PrincipalOAuth2Details.java +++ b/src/main/java/com/example/reddiserver/security/oauth/PrincipalOAuth2Details.java @@ -44,6 +44,6 @@ public String getAuthority() { @Override public String getName() { - return "name"; + return member.getProviderId(); } } From a87897a752da0b4785009b43d274f3ea8f1a18c0 Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 14:55:34 +0900 Subject: [PATCH 8/9] =?UTF-8?q?chore=20:=20redis=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- .../reddiserver/config/RedisConfig.java | 34 +++++++++++++++++++ src/main/resources/application.yml | 4 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/example/reddiserver/config/RedisConfig.java diff --git a/build.gradle b/build.gradle index 238bbe7..7ae2b40 100644 --- a/build.gradle +++ b/build.gradle @@ -42,8 +42,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - // Swagger 3.0.0 implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis' } tasks.named('test') { diff --git a/src/main/java/com/example/reddiserver/config/RedisConfig.java b/src/main/java/com/example/reddiserver/config/RedisConfig.java new file mode 100644 index 0000000..fb03ad6 --- /dev/null +++ b/src/main/java/com/example/reddiserver/config/RedisConfig.java @@ -0,0 +1,34 @@ +package com.example.reddiserver.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; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String redisHost; + + @Value("${spring.data.redis.port}") + private int redisPort; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(redisHost, redisPort); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + return redisTemplate; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a7a76a5..7c1c684 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -27,6 +27,10 @@ spring: client-id: ${CLIENT_ID} client-secret: ${CLIENT_SECRET} scope: profile, email + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} springdoc: api-docs: From e5aedd0c20ec6767155c61876d6c92ae8f6548ae Mon Sep 17 00:00:00 2001 From: younggyo Date: Thu, 11 Jan 2024 14:56:09 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat=20:=20redis=20=ED=99=9C=EC=9A=A9=20ref?= =?UTF-8?q?reshToken=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reddiserver/entity/RefreshToken.java | 25 ++++++++----------- .../repository/RefreshTokenRepository.java | 8 +++--- .../security/jwt/TokenProvider.java | 9 ++++--- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/example/reddiserver/entity/RefreshToken.java b/src/main/java/com/example/reddiserver/entity/RefreshToken.java index 3bbff62..24da08c 100644 --- a/src/main/java/com/example/reddiserver/entity/RefreshToken.java +++ b/src/main/java/com/example/reddiserver/entity/RefreshToken.java @@ -1,30 +1,25 @@ package com.example.reddiserver.entity; -import com.example.reddiserver.entity.base.BaseTimeEntity; -import jakarta.persistence.*; -import lombok.AccessLevel; import lombok.Builder; 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.index.Indexed; -@Entity @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class RefreshToken extends BaseTimeEntity { +@RedisHash(value = "refreshToken", timeToLive = 60*60*24*3) +public class RefreshToken { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private String refreshToken; - @Column(nullable = false, unique = true) - private String email; + @Indexed + private String providerId; - @Column(nullable = false) - private String refreshToken; @Builder - public RefreshToken(String email, String refreshToken) { - this.email = email; + public RefreshToken(String providerId, String refreshToken) { + this.providerId = providerId; this.refreshToken = refreshToken; } diff --git a/src/main/java/com/example/reddiserver/repository/RefreshTokenRepository.java b/src/main/java/com/example/reddiserver/repository/RefreshTokenRepository.java index 7e54336..b77330f 100644 --- a/src/main/java/com/example/reddiserver/repository/RefreshTokenRepository.java +++ b/src/main/java/com/example/reddiserver/repository/RefreshTokenRepository.java @@ -1,10 +1,12 @@ package com.example.reddiserver.repository; import com.example.reddiserver.entity.RefreshToken; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; import java.util.Optional; -public interface RefreshTokenRepository extends JpaRepository { - Optional findByEmail(String email); +@Repository +public interface RefreshTokenRepository extends CrudRepository { + Optional findByProviderId(String providerId); } diff --git a/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java b/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java index 82b3a12..91b0c5c 100644 --- a/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java +++ b/src/main/java/com/example/reddiserver/security/jwt/TokenProvider.java @@ -27,7 +27,7 @@ public class TokenProvider implements InitializingBean { private static final String AUTHORITIES_KEY = "auth"; private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30L; - private static final long REFRESH_TOKEN_EXPIRE_TIME = 7 * 242 * 60 * 60 * 1000L; + private static final long REFRESH_TOKEN_EXPIRE_TIME = 60 * 60 * 24 * 3L; @Value("${spring.jwt.secret}") private String secret; @@ -65,17 +65,18 @@ public TokenDto createAccessToken(Authentication authentication) { String accessToken = createToken(authentication.getName(), authorities, "access"); String newRefreshToken = createToken(authentication.getName(), authorities, "refresh"); - Optional oldRefreshToken = refreshTokenRepository.findByEmail(authentication.getName()); + Optional oldRefreshToken = refreshTokenRepository.findByProviderId(authentication.getName()); if (oldRefreshToken.isPresent()) { refreshTokenRepository.save(oldRefreshToken.get().updateToken(newRefreshToken)); } else { RefreshToken refreshToken = RefreshToken.builder() - .email(authentication.getName()) + .providerId(authentication.getName()) .refreshToken(newRefreshToken) .build(); refreshTokenRepository.save(refreshToken); + System.out.println("test"); } return TokenDto.builder() @@ -129,7 +130,7 @@ public boolean validateAccessToken(String accessToken) { public boolean refreshTokenValidation(String token) { if (!validateAccessToken(token)) return false; - Optional refreshToken = refreshTokenRepository.findByEmail(getEmailFromToken(token)); + Optional refreshToken = refreshTokenRepository.findByProviderId(getEmailFromToken(token)); return refreshToken.isPresent() && token.equals(refreshToken.get().getRefreshToken()); }