diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73235de..e259412 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,12 @@ name: Tradin_CI + on: push: branches: - - dev - pull_request: - types: [ opened, synchronize, reopened ] + - '**' jobs: - build: - name: Build and analyze + build-springboot: + name: Build and analyze (SpringBoot) runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -20,22 +19,16 @@ jobs: java-version: 17 distribution: 'temurin' - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Cache Gradle packages uses: actions/cache@v3 with: - path: ~/.gradle/caches + path: | + ~/.gradle/caches + ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle - - name: Build and analyze + - name: Build and analyze (SpringBoot) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew clean build sonarqube --info \ No newline at end of file + run: | + ./gradlew clean build \ No newline at end of file diff --git a/src/main/java/com/tradin/common/config/SecurityConfiguration.java b/src/main/java/com/tradin/common/config/SecurityConfiguration.java index 6f2721a..9ea5e4c 100644 --- a/src/main/java/com/tradin/common/config/SecurityConfiguration.java +++ b/src/main/java/com/tradin/common/config/SecurityConfiguration.java @@ -5,13 +5,13 @@ import com.tradin.common.jwt.JwtUtil; import com.tradin.common.utils.PasswordEncoder; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; +import lombok.val; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -23,69 +23,44 @@ @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfiguration { + private final JwtUtil jwtUtil; private final PasswordEncoder passwordEncoder; - - @Value("${secret.swagger-username}") - private String swaggerUsername; - - @Value("${secret.swagger-password}") - private String swaggerPassword; - - private final String[] SWAGGER_PATTERN = {"/swagger-ui", "/api-docs"}; + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final JwtExceptionFilter jwtExceptionFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - return http.build(); -// http -// .authorizeRequests() -// .mvcMatchers("/health-check").permitAll() -// .mvcMatchers("/v1/auth/cognito", "/v1/auth/token").permitAll() -// .mvcMatchers("/v1/strategies/future", "/v1/strategies/spot").permitAll() -// .mvcMatchers("/v1/histories").permitAll() -// .mvcMatchers(SWAGGER_PATTERN).authenticated() -// .anyRequest().authenticated() -// .and() -// .cors().configurationSource(corsConfigurationSource()) -// .and() -// .httpBasic() -// .and() -// .formLogin().disable() -// .logout().disable() -// .csrf().disable() -// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) -// .and() -// .headers() -// .frameOptions().sameOrigin() -// .httpStrictTransportSecurity().disable() -//// .and() -//// .exceptionHandling() -//// .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) -// .and() -// .addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) -// .addFilterBefore(new JwtExceptionFilter(), JwtAuthenticationFilter.class) -// .build(); + return http.csrf(AbstractHttpConfigurer::disable) + .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource())) + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement( + session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ).authorizeHttpRequests(authorizeRequest -> + authorizeRequest + .requestMatchers("/v1/auth/test/token").permitAll() + .anyRequest().authenticated() + ) + .headers(headers -> headers + .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin) + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtExceptionFilter, JwtAuthenticationFilter.class) + .build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); + val configuration = new CorsConfiguration(); - configuration.addAllowedOriginPattern("*"); + configuration.addAllowedOrigin("*"); configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); - configuration.setAllowCredentials(true); + configuration.setAllowCredentials(false); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + val source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); - return source; } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - - auth.inMemoryAuthentication() - .withUser(swaggerUsername).password(passwordEncoder.encode(swaggerPassword)).roles("SWAGGER"); - } } \ No newline at end of file diff --git a/src/main/java/com/tradin/common/exception/ExceptionType.java b/src/main/java/com/tradin/common/exception/ExceptionType.java index 6f76e65..75c30d0 100644 --- a/src/main/java/com/tradin/common/exception/ExceptionType.java +++ b/src/main/java/com/tradin/common/exception/ExceptionType.java @@ -24,6 +24,10 @@ public enum ExceptionType { WRONG_PASSWORD_EXCEPTION(UNAUTHORIZED, "비밀번호가 일치하지 않습니다."), EMAIL_ALREADY_EXISTS_EXCEPTION(UNAUTHORIZED, "이미 존재하는 이메일입니다."), NOT_FOUND_JWK_PARTS_EXCEPTION(UNAUTHORIZED, "존재하지 않는 kid입니다."), + INVALID_JWT_SIGNATURE_EXCEPTION(UNAUTHORIZED, "유효하지 않은 JWT 서명입니다."), + EXPIRED_JWT_TOKEN_EXCEPTION(UNAUTHORIZED, "만료된 JWT 토큰입니다."), + UNSUPPORTED_JWT_TOKEN_EXCEPTION(UNAUTHORIZED, "지원하지 않는 JWT 토큰입니다."), + NOT_FOUND_JWT_CLAIMS_EXCEPTION(UNAUTHORIZED, "JWT Claims가 존재하지 않습니다."), //403 Forbidden @@ -46,8 +50,7 @@ public enum ExceptionType { DECRYPT_FAIL_EXCEPTION(INTERNAL_SERVER_ERROR, "복호화에 실패하였습니다."), SIGNATURE_GENERATION_FAIL_EXCEPTION(INTERNAL_SERVER_ERROR, "JWT 서명 생성에 실패하였습니다."), PUBLIC_KEY_GENERATE_FAIL_EXCEPTION(INTERNAL_SERVER_ERROR, "공개키 생성에 실패하였습니다."), - INTERNAL_SERVER_ERROR_EXCEPTION(INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); - ; + INTERNAL_SERVER_ERROR_EXCEPTION(INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");; private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/tradin/common/filter/JwtAuthenticationFilter.java b/src/main/java/com/tradin/common/filter/JwtAuthenticationFilter.java index 9f6a528..f7a8520 100644 --- a/src/main/java/com/tradin/common/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/tradin/common/filter/JwtAuthenticationFilter.java @@ -13,26 +13,30 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; +@Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { - private static final String AUTHORIZATION_HEADER_PREFIX = "Authorization"; + + private static final List ALLOW_LIST = List.of("/auth", "/swagger-ui", "/api-docs", "/health-check", + "/notifications" + ); public static final String BEARER_PREFIX = "Bearer "; + public static final String AUTHORIZATION_HEADER_PREFIX = "Authorization"; + private final JwtUtil jwtUtil; - private static final List ALLOW_LIST = List.of("/swagger-ui", "/api-docs", "/health-check", "/v1/auth/cognito", "/v1/auth/token", "/v1/strategies/future", "/v1/strategies/spot", "/v1/histories"); @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 (!isAllowList(request.getRequestURI())) { String bearerToken = request.getHeader(AUTHORIZATION_HEADER_PREFIX); - String sub = validateHeaderAndGetSub(bearerToken); - setAuthentication(sub); + Long userId = validateHeaderAndGetUserId(bearerToken); + setAuthentication(userId); } - filterChain.doFilter(request, response); } @@ -40,10 +44,11 @@ private boolean isAllowList(String requestURI) { return ALLOW_LIST.stream().anyMatch(requestURI::contains); } - private String validateHeaderAndGetSub(String bearerToken) { + + private Long validateHeaderAndGetUserId(String bearerToken) { validateHasText(bearerToken); validateStartWithBearer(bearerToken); - return validateAccessTokenAndGetSub(getAccessTokenFromBearer(bearerToken)); + return validateAccessToken(getAccessTokenFromBearer(bearerToken)); } private void validateHasText(String bearerToken) { @@ -58,15 +63,15 @@ private void validateStartWithBearer(String bearerToken) { } } - private String validateAccessTokenAndGetSub(String accessToken) { - return jwtUtil.validateToken(accessToken).get("sub", String.class); + private Long validateAccessToken(String accessToken) { + return jwtUtil.validateAccessToken(accessToken); } private String getAccessTokenFromBearer(String bearerToken) { return bearerToken.substring(BEARER_PREFIX.length()); } - private void setAuthentication(String sub) { - SecurityContextHolder.getContext().setAuthentication(jwtUtil.getAuthentication(sub)); + private void setAuthentication(Long userId) { + SecurityContextHolder.getContext().setAuthentication(jwtUtil.getAuthentication(userId)); } } diff --git a/src/main/java/com/tradin/common/filter/JwtExceptionFilter.java b/src/main/java/com/tradin/common/filter/JwtExceptionFilter.java index 8e717ce..09d4f2a 100644 --- a/src/main/java/com/tradin/common/filter/JwtExceptionFilter.java +++ b/src/main/java/com/tradin/common/filter/JwtExceptionFilter.java @@ -1,43 +1,40 @@ package com.tradin.common.filter; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.tradin.common.exception.TradinException; +import com.tradin.common.exception.ExceptionType; +import com.tradin.common.response.TradinResponse; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; +@RequiredArgsConstructor +@Component public class JwtExceptionFilter extends OncePerRequestFilter { + @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) + throws ServletException, IOException { try { - filterChain.doFilter(request, response); - } catch (TradinException e) { - respondException(response, e); + filterChain.doFilter(httpServletRequest, httpServletResponse); + } catch (Exception e) { + httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); + httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpServletResponse.setCharacterEncoding("UTF-8"); + httpServletResponse.getWriter() + .write(toJson(TradinResponse.error(ExceptionType.INVALID_JWT_TOKEN_EXCEPTION, e.getMessage()))); } } - private void respondException(HttpServletResponse response, TradinException e) throws IOException { - setResponseHeader(response); - writeResponse(response, e); - } - - private void setResponseHeader(HttpServletResponse response) { - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - } - - private void writeResponse(HttpServletResponse response, TradinException e) throws IOException { - response.setStatus(e.getErrorType().getHttpStatus().value()); - response.getWriter().write(toJson(e.getErrorType().getHttpStatus())); - } - - private String toJson(HttpStatus exceptionResponse) throws JsonProcessingException { - return new ObjectMapper().writeValueAsString(exceptionResponse); + private String toJson(TradinResponse response) throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(response); } -} +} \ No newline at end of file diff --git a/src/main/java/com/tradin/common/jwt/JwtProvider.java b/src/main/java/com/tradin/common/jwt/JwtProvider.java new file mode 100644 index 0000000..1c9f7eb --- /dev/null +++ b/src/main/java/com/tradin/common/jwt/JwtProvider.java @@ -0,0 +1,46 @@ +package com.tradin.common.jwt; + +import com.tradin.module.auth.controller.dto.response.TokenResponseDto; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class JwtProvider { + + private final RedisTemplate redisTemplate; + private final JwtSecretKeyProvider jwtSecretKeyProvider; + + private static final long ACCESS_TOKEN_EXPIRE_TIME = 24 * 60 * 60 * 1000L; // 1일 + private static final long REFRESH_TOKEN_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; // 7일 + + public TokenResponseDto createJwtToken(Long userId) { + long now = (new Date()).getTime(); + Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME); + Date refreshTokenExpiresIn = new Date(now + REFRESH_TOKEN_EXPIRE_TIME); + + // Access Token 생성 + String accessToken = Jwts.builder() + .claim("USER_ID", String.valueOf(userId)) + .setExpiration(accessTokenExpiresIn) + .signWith(jwtSecretKeyProvider.getSecretKey(), SignatureAlgorithm.HS512) + .compact(); + + // Refresh Token 생성 + String refreshToken = Jwts.builder() + .setExpiration(refreshTokenExpiresIn) + .signWith(jwtSecretKeyProvider.getSecretKey(), SignatureAlgorithm.HS512) + .compact(); + + redisTemplate.opsForValue().set("RT:" + userId, refreshToken, REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS); + + return TokenResponseDto.of(accessToken, refreshToken); + } +} \ No newline at end of file diff --git a/src/main/java/com/tradin/common/jwt/JwtSecretKeyProvider.java b/src/main/java/com/tradin/common/jwt/JwtSecretKeyProvider.java new file mode 100644 index 0000000..18396f0 --- /dev/null +++ b/src/main/java/com/tradin/common/jwt/JwtSecretKeyProvider.java @@ -0,0 +1,22 @@ +package com.tradin.common.jwt; + +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import java.security.Key; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JwtSecretKeyProvider { + + private final Key secretKey; + + public JwtSecretKeyProvider(@Value("${JWT_SECRET_KEY}") String secretKey) { + byte[] keyBytes = Decoders.BASE64.decode(secretKey); + this.secretKey = Keys.hmacShaKeyFor(keyBytes); + } + + public Key getSecretKey() { + return secretKey; + } +} diff --git a/src/main/java/com/tradin/common/jwt/JwtUtil.java b/src/main/java/com/tradin/common/jwt/JwtUtil.java index 8974514..3a0ed1b 100644 --- a/src/main/java/com/tradin/common/jwt/JwtUtil.java +++ b/src/main/java/com/tradin/common/jwt/JwtUtil.java @@ -1,148 +1,105 @@ package com.tradin.common.jwt; -import com.google.gson.JsonParser; +import static com.tradin.common.exception.ExceptionType.DIFFERENT_REFRESH_TOKEN_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.EXPIRED_JWT_TOKEN_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.INVALID_JWT_SIGNATURE_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.INVALID_JWT_TOKEN_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.NOT_FOUND_JWT_CLAIMS_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.NOT_FOUND_JWT_USERID_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.NOT_FOUND_REFRESH_TOKEN_EXCEPTION; +import static com.tradin.common.exception.ExceptionType.UNSUPPORTED_JWT_TOKEN_EXCEPTION; + import com.tradin.common.exception.TradinException; -import com.tradin.module.auth.service.dto.UserDataDto; -import com.tradin.module.feign.client.dto.cognito.JwkDto; -import com.tradin.module.feign.client.dto.cognito.JwkDtos; -import com.tradin.module.feign.service.CognitoFeignService; import com.tradin.module.users.domain.Users; import com.tradin.module.users.service.UsersService; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.security.KeyFactory; -import java.security.PublicKey; -import java.security.spec.RSAPublicKeySpec; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static com.tradin.common.exception.ExceptionType.*; @Slf4j @Component -@Transactional @RequiredArgsConstructor public class JwtUtil { -// @Value("${secret.cognito-issuer}") -// private final String cognitoIssuer; - private static final long JWK_KEY_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; // 7일 + private final JwtSecretKeyProvider jwtSecretKeyProvider; private final UsersService userService; private final RedisTemplate redisTemplate; - private final CognitoFeignService cognitoFeignService; - public UserDataDto extractUserDataFromIdToken(String idToken) { - Claims claims = validateToken(idToken); + public Long validateTokensAndGetUserId(String accessToken, String refreshToken) { + validateTokenClaims(refreshToken); + return getUserIdFromTokens(accessToken, refreshToken); + } - String sub = claims.get("sub", String.class); - String email = claims.get("email", String.class); - String socialId = claims.get("cognito:username", String.class); + private void validateTokenClaims(String token) { + parseClaim(token); + } - return UserDataDto.of(sub, email, socialId); + private Long getUserIdFromTokens(String accessToken, String refreshToken) { + Long userId = getUserIdFromAccessToken(accessToken); + validateExistRefreshToken(refreshToken, userId); + return userId; } - private Claims parseClaim(String token, PublicKey publicKey) { + private void validateExistRefreshToken(String refreshToken, Long userId) { + Object refreshTokenFromDb = redisTemplate.opsForValue().get("RT:" + userId); - try { - return Jwts.parserBuilder() - .setSigningKey(publicKey) - //.requireIssuer(cognitoIssuer) - .build() - .parseClaimsJws(token) - .getBody(); - } catch (Exception e) { - throw new TradinException(INVALID_JWT_TOKEN_EXCEPTION); + if (refreshTokenFromDb == null) { + throw new TradinException(NOT_FOUND_REFRESH_TOKEN_EXCEPTION); } - } - private String getKidFromTokenHeader(String token) { - String[] parts = token.split("\\."); // Split the token into parts - if (parts.length > 1) { - String header = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); // Decode the header - return JsonParser.parseString(header).getAsJsonObject().get("kid").getAsString(); // Parse the JSON and get the "kid" field - } else { - throw new TradinException(INVALID_JWT_TOKEN_EXCEPTION); + if (!refreshToken.equals(refreshTokenFromDb)) { + throw new TradinException(DIFFERENT_REFRESH_TOKEN_EXCEPTION); } } - public Claims validateToken(String token) { - String kidFromTokenHeader = getKidFromTokenHeader(token); - Map jwtKeyParts = getNAndEFromCachedJwkKey(kidFromTokenHeader).orElseThrow(() -> new TradinException(NOT_FOUND_JWK_PARTS_EXCEPTION)); - String n = jwtKeyParts.get("n"); - String e = jwtKeyParts.get("e"); - PublicKey publicKey = generateRSAPublicKey(n, e); - - return parseClaim(token, publicKey); - } - - private Optional> getNAndEFromCachedJwkKey(String kid) { - final String cacheKey = "JwkKey:Kid" + kid; - JwkDto jwkDto = getJwkFromCache(cacheKey); - - if (jwkDto == null) { - cacheJwkKey(); - jwkDto = getJwkFromCache(cacheKey); + private Claims parseClaim(String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(jwtSecretKeyProvider.getSecretKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (SecurityException e) { + throw new TradinException(INVALID_JWT_SIGNATURE_EXCEPTION); + } catch (MalformedJwtException e) { + throw new TradinException(INVALID_JWT_TOKEN_EXCEPTION); + } catch (ExpiredJwtException e) { + throw new TradinException(EXPIRED_JWT_TOKEN_EXCEPTION); + } catch (UnsupportedJwtException e) { + throw new TradinException(UNSUPPORTED_JWT_TOKEN_EXCEPTION); + } catch (IllegalArgumentException e) { + throw new TradinException(NOT_FOUND_JWT_CLAIMS_EXCEPTION); } - - return Optional.ofNullable(jwkDto).map(dto -> { - Map map = new HashMap<>(); - map.put("n", dto.getN()); - map.put("e", dto.getE()); - return map; - }); } - - private JwkDto getJwkFromCache(String cacheKey) { - return (JwkDto) redisTemplate.opsForValue().get(cacheKey); + public Long getUserIdFromAccessToken(String accessToken) { + Long userId = Long.valueOf(parseClaim(accessToken).get("USER_ID", String.class)); + validateExistUserIdFromAccessToken(userId); + return userId; } - private void cacheJwkKey() { - JwkDtos jwkDtos = getJwkKeyFromCognito(); - - for (JwkDto jwkDto : jwkDtos.getKeys()) { - final String cacheKey = "JwkKey:Kid" + jwkDto.getKid(); - redisTemplate.opsForValue().set(cacheKey, jwkDto, JWK_KEY_EXPIRE_TIME, TimeUnit.MILLISECONDS); + private void validateExistUserIdFromAccessToken(Long userId) { + if (userId == null) { + throw new TradinException(NOT_FOUND_JWT_USERID_EXCEPTION); } } - private JwkDtos getJwkKeyFromCognito() { - return cognitoFeignService.getJwkKey(); - } - - private PublicKey generateRSAPublicKey(String n, String e) { - try { - byte[] nBytes = Base64.getUrlDecoder().decode(n); - byte[] eBytes = Base64.getUrlDecoder().decode(e); - - BigInteger nBigInt = new BigInteger(1, nBytes); - BigInteger eBigInt = new BigInteger(1, eBytes); - - RSAPublicKeySpec spec = new RSAPublicKeySpec(nBigInt, eBigInt); - KeyFactory factory = KeyFactory.getInstance("RSA"); - - return factory.generatePublic(spec); - - } catch (Exception ex) { - throw new TradinException(PUBLIC_KEY_GENERATE_FAIL_EXCEPTION); - } + public Long validateAccessToken(String accessToken) { + Long userId = Long.valueOf(parseClaim(accessToken).get("USER_ID", String.class)); + validateExistUserIdFromAccessToken(userId); + return userId; } - public Authentication getAuthentication(String sub) { - Users user = userService.loadUserByUsername(sub); + public Authentication getAuthentication(Long userId) { + Users user = userService.loadUserByUsername(String.valueOf(userId)); return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); } } \ No newline at end of file diff --git a/src/main/java/com/tradin/module/auth/controller/AuthController.java b/src/main/java/com/tradin/module/auth/controller/AuthController.java index ec990c6..5ccc5d0 100644 --- a/src/main/java/com/tradin/module/auth/controller/AuthController.java +++ b/src/main/java/com/tradin/module/auth/controller/AuthController.java @@ -2,32 +2,44 @@ import com.tradin.common.annotation.DisableAuthInSwagger; import com.tradin.module.auth.controller.dto.request.TokenReissueRequestDto; +import com.tradin.module.auth.controller.dto.response.TokenResponseDto; import com.tradin.module.auth.service.AuthService; -import com.tradin.module.users.controller.dto.response.TokenResponseDto; import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor @RequestMapping(value = "/v1/auth") public class AuthController { + private final AuthService authService; @Operation(summary = "구글 로그인&회원가입") @DisableAuthInSwagger - @GetMapping("/cognito") + @GetMapping("/google") public ResponseEntity auth(@RequestParam String code) { return ResponseEntity.ok(authService.auth(code)); } - @DisableAuthInSwagger @Operation(summary = "엑세스 토큰 재발급") + @DisableAuthInSwagger @PostMapping("/token") - public ResponseEntity reissueToken(@Valid @RequestBody TokenReissueRequestDto request) { + public ResponseEntity reissueToken(@Valid @RequestBody TokenReissueRequestDto request) { return ResponseEntity.ok(authService.reissueToken(request.toServiceDto())); } + + @Operation(summary = "테스트 토큰 발급") + @DisableAuthInSwagger + @GetMapping("/test/token") + public ResponseEntity issueTestToken() { + return ResponseEntity.ok(authService.issueTestToken()); + } } diff --git a/src/main/java/com/tradin/module/auth/controller/dto/request/TokenReissueRequestDto.java b/src/main/java/com/tradin/module/auth/controller/dto/request/TokenReissueRequestDto.java index 6ae442e..3b1fee4 100644 --- a/src/main/java/com/tradin/module/auth/controller/dto/request/TokenReissueRequestDto.java +++ b/src/main/java/com/tradin/module/auth/controller/dto/request/TokenReissueRequestDto.java @@ -1,20 +1,23 @@ package com.tradin.module.auth.controller.dto.request; import com.tradin.module.auth.service.dto.TokenReissueDto; +import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import jakarta.validation.constraints.NotBlank; - @AllArgsConstructor @NoArgsConstructor @Getter public class TokenReissueRequestDto { + + @NotBlank(message = "Access Token must not be blank") + private String accessToken; + @NotBlank(message = "Refresh Token must not be blank") private String refreshToken; public TokenReissueDto toServiceDto() { - return TokenReissueDto.of(refreshToken); + return TokenReissueDto.of(accessToken, refreshToken); } } diff --git a/src/main/java/com/tradin/module/users/controller/dto/response/TokenResponseDto.java b/src/main/java/com/tradin/module/auth/controller/dto/response/TokenResponseDto.java similarity index 87% rename from src/main/java/com/tradin/module/users/controller/dto/response/TokenResponseDto.java rename to src/main/java/com/tradin/module/auth/controller/dto/response/TokenResponseDto.java index 567f90d..95bf0ae 100644 --- a/src/main/java/com/tradin/module/users/controller/dto/response/TokenResponseDto.java +++ b/src/main/java/com/tradin/module/auth/controller/dto/response/TokenResponseDto.java @@ -1,4 +1,4 @@ -package com.tradin.module.users.controller.dto.response; +package com.tradin.module.auth.controller.dto.response; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -7,6 +7,7 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter public class TokenResponseDto { + private String accessToken; private String refreshToken; diff --git a/src/main/java/com/tradin/module/auth/service/AuthService.java b/src/main/java/com/tradin/module/auth/service/AuthService.java index 528f91c..8f45cea 100644 --- a/src/main/java/com/tradin/module/auth/service/AuthService.java +++ b/src/main/java/com/tradin/module/auth/service/AuthService.java @@ -1,52 +1,50 @@ package com.tradin.module.auth.service; +import static com.tradin.module.users.domain.UserSocialType.GOOGLE; + +import com.tradin.common.jwt.JwtProvider; import com.tradin.common.jwt.JwtUtil; +import com.tradin.module.auth.controller.dto.response.TokenResponseDto; import com.tradin.module.auth.service.dto.TokenReissueDto; import com.tradin.module.auth.service.dto.UserDataDto; -import com.tradin.module.feign.client.dto.cognito.TokenDto; -import com.tradin.module.feign.service.CognitoFeignService; -import com.tradin.module.users.controller.dto.response.TokenResponseDto; +import com.tradin.module.users.domain.Users; import com.tradin.module.users.service.UsersService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.tradin.module.users.domain.UserSocialType.GOOGLE; - @Service @Transactional @RequiredArgsConstructor public class AuthService { + + private final GoogleAuthService googleAuthService; private final UsersService usersService; - private final CognitoFeignService cognitoFeignService; private final JwtUtil jwtUtil; + private final JwtProvider jwtProvider; public TokenResponseDto auth(String code) { - TokenDto token = getTokenFromCognito(code); - String idToken = token.getId_token(); - saveUser(extractUserDataFromIdToken(idToken)); - - return TokenResponseDto.of(token.getAccess_token(), token.getRefresh_token()); - } + UserDataDto userDataDto = googleAuthService.getUserInfo(code); + Users user = saveOrGetUser(userDataDto); - public String reissueToken(TokenReissueDto tokenReissueDto) { - return reissueAccessTokenFromCognito(tokenReissueDto.getRefreshToken()); + return createJwtToken(user.getId()); } - private TokenDto getTokenFromCognito(String code) { - return cognitoFeignService.getTokenFromCognito(code); + public TokenResponseDto reissueToken(TokenReissueDto request) { + Long id = jwtUtil.validateTokensAndGetUserId(request.accessToken(), request.refreshToken()); + return jwtProvider.createJwtToken(id); } - private String reissueAccessTokenFromCognito(String refreshToken) { - return cognitoFeignService.reissueAccessTokenFromCognito(refreshToken); + public TokenResponseDto issueTestToken() { + return jwtProvider.createJwtToken(1L); } - private UserDataDto extractUserDataFromIdToken(String idToken) { - return jwtUtil.extractUserDataFromIdToken(idToken); + private TokenResponseDto createJwtToken(Long userId) { + return jwtProvider.createJwtToken(userId); } - private void saveUser(UserDataDto userDataDto) { - usersService.saveUser(userDataDto, GOOGLE); + private Users saveOrGetUser(UserDataDto userDataDto) { + return usersService.saveOrGetUser(userDataDto, GOOGLE); } } diff --git a/src/main/java/com/tradin/module/auth/service/GoogleAuthService.java b/src/main/java/com/tradin/module/auth/service/GoogleAuthService.java new file mode 100644 index 0000000..4c558b2 --- /dev/null +++ b/src/main/java/com/tradin/module/auth/service/GoogleAuthService.java @@ -0,0 +1,51 @@ +package com.tradin.module.auth.service; + +import com.tradin.module.auth.service.dto.GoogleAccessTokenResponseDto; +import com.tradin.module.auth.service.dto.UserDataDto; +import java.util.LinkedHashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@RequiredArgsConstructor +@Service +public class GoogleAuthService { + + private final RestTemplate authClient = new RestTemplate(); + @Value("${google.login.client_id}") + private String googleClientId; + + @Value("${google.login.client_secret}") + private String googleClientSecret; + + @Value("${google.login.redirect_uri}") + private String googleRedirectUri; + + public UserDataDto getUserInfo(String authorizationCode) { + String host = "https://oauth2.googleapis.com/tokeninfo?id_token="; + String accessToken = getGoogleAccessToken(authorizationCode); + String url = host + accessToken; + + return authClient.getForObject(url, UserDataDto.class); + } + + private String getGoogleAccessToken(String authorizationCode) { + Map params = new LinkedHashMap<>(); + params.put("client_id", googleClientId); + params.put("client_secret", googleClientSecret); + params.put("code", authorizationCode); + params.put("grant_type", "authorization_code"); + params.put("redirect_uri", googleRedirectUri); + String host = "https://oauth2.googleapis.com/token"; + + GoogleAccessTokenResponseDto googleAccessTokenResponseDto = authClient.postForObject( + host, + params, + GoogleAccessTokenResponseDto.class + ); + + return googleAccessTokenResponseDto.accessToken(); + } +} diff --git a/src/main/java/com/tradin/module/auth/service/dto/GoogleAccessTokenResponseDto.java b/src/main/java/com/tradin/module/auth/service/dto/GoogleAccessTokenResponseDto.java new file mode 100644 index 0000000..f238605 --- /dev/null +++ b/src/main/java/com/tradin/module/auth/service/dto/GoogleAccessTokenResponseDto.java @@ -0,0 +1,9 @@ +package com.tradin.module.auth.service.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record GoogleAccessTokenResponseDto( + @JsonProperty("access_token") String accessToken +) { + +} \ No newline at end of file diff --git a/src/main/java/com/tradin/module/auth/service/dto/TokenReissueDto.java b/src/main/java/com/tradin/module/auth/service/dto/TokenReissueDto.java index ee9605e..d9c63e5 100644 --- a/src/main/java/com/tradin/module/auth/service/dto/TokenReissueDto.java +++ b/src/main/java/com/tradin/module/auth/service/dto/TokenReissueDto.java @@ -1,14 +1,8 @@ package com.tradin.module.auth.service.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; +public record TokenReissueDto(String accessToken, String refreshToken) { -@AllArgsConstructor -@Getter -public class TokenReissueDto { - private final String refreshToken; - - public static TokenReissueDto of(String refreshToken) { - return new TokenReissueDto(refreshToken); + public static TokenReissueDto of(String accessToken, String refreshToken) { + return new TokenReissueDto(accessToken, refreshToken); } } diff --git a/src/main/java/com/tradin/module/feign/client/CognitoClient.java b/src/main/java/com/tradin/module/feign/client/CognitoClient.java deleted file mode 100644 index 089f95d..0000000 --- a/src/main/java/com/tradin/module/feign/client/CognitoClient.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.tradin.module.feign.client; - -import com.tradin.module.feign.client.dto.cognito.AuthDto; -import com.tradin.module.feign.client.dto.cognito.ReissueAccessTokenDto; -import com.tradin.module.feign.client.dto.cognito.ReissuedTokenDto; -import com.tradin.module.feign.client.dto.cognito.TokenDto; - -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.messaging.handler.annotation.Headers; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; - -@FeignClient(name = "cognitoClient", url = "https://tradin.auth.ap-northeast-2.amazoncognito.com") -public interface CognitoClient { - @PostMapping(value = "/oauth2/token", consumes = "application/x-www-form-urlencoded") - @feign.Headers("Content-Type: application/x-www-form-urlencoded") - TokenDto getAccessAndRefreshToken(@RequestBody AuthDto authDto); - - @PostMapping(value = "/oauth2/token", consumes = "application/x-www-form-urlencoded") - ReissuedTokenDto reissueRefreshToken(@RequestBody ReissueAccessTokenDto reissueAccessTokenDto); -} diff --git a/src/main/java/com/tradin/module/feign/client/CognitoJwkFeignClient.java b/src/main/java/com/tradin/module/feign/client/CognitoJwkFeignClient.java deleted file mode 100644 index 8fcfc4b..0000000 --- a/src/main/java/com/tradin/module/feign/client/CognitoJwkFeignClient.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.tradin.module.feign.client; - -import com.tradin.module.feign.client.dto.cognito.JwkDtos; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; - -@FeignClient(name = "cognitoJwkClient", url = "https://cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-2_45OUbYhf2/.well-known/jwks.json") -public interface CognitoJwkFeignClient { - - @GetMapping("") - JwkDtos getJwks(); -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/AuthDto.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/AuthDto.java deleted file mode 100644 index ef1ffa6..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/AuthDto.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@AllArgsConstructor -@NoArgsConstructor -public class AuthDto { - private String grant_type; - private String client_id; - private String redirect_uri; - private String code; - - public static AuthDto of(String grant_type, String client_id, String redirect_uri, String code) { - return new AuthDto(grant_type, client_id, redirect_uri, code); - } -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDto.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDto.java deleted file mode 100644 index ab86073..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@AllArgsConstructor -@NoArgsConstructor -public class JwkDto { - private String alg; - private String e; - private String kid; - private String kty; - private String n; - private String use; -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDtos.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDtos.java deleted file mode 100644 index 42da871..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/JwkDtos.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@AllArgsConstructor -@NoArgsConstructor -public class JwkDtos { - List keys; -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissueAccessTokenDto.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissueAccessTokenDto.java deleted file mode 100644 index 16d3abe..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissueAccessTokenDto.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@AllArgsConstructor -public class ReissueAccessTokenDto { - private String grant_type; - private String client_id; - private String refresh_token; -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissuedTokenDto.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissuedTokenDto.java deleted file mode 100644 index c6ad336..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/ReissuedTokenDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@AllArgsConstructor -public class ReissuedTokenDto { - private final String idToken; - private final String accessToken; - private final String expiresIn; - private final String tokenType; -} \ No newline at end of file diff --git a/src/main/java/com/tradin/module/feign/client/dto/cognito/TokenDto.java b/src/main/java/com/tradin/module/feign/client/dto/cognito/TokenDto.java deleted file mode 100644 index 8bdf992..0000000 --- a/src/main/java/com/tradin/module/feign/client/dto/cognito/TokenDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tradin.module.feign.client.dto.cognito; - - -import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import com.fasterxml.jackson.databind.annotation.JsonNaming; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@AllArgsConstructor -@Getter -@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -public class TokenDto { - private String id_token; - private String access_token; - private String refresh_token; - private String expires_in; - private String token_type; -} diff --git a/src/main/java/com/tradin/module/feign/client/dto/google/AccessTokenRequestDto.java b/src/main/java/com/tradin/module/feign/client/dto/google/AccessTokenRequestDto.java new file mode 100644 index 0000000..3569c06 --- /dev/null +++ b/src/main/java/com/tradin/module/feign/client/dto/google/AccessTokenRequestDto.java @@ -0,0 +1,14 @@ +package com.tradin.module.feign.client.dto.google; + +public record AccessTokenRequestDto( + String clientId, + String clientSecret, + String code, + String redirectUri, + String grantType +) { + + public static AccessTokenRequestDto of(String clientId, String clientSecret, String code, String redirectUri) { + return new AccessTokenRequestDto(clientId, clientSecret, code, redirectUri, "authorization_code"); + } +} diff --git a/src/main/java/com/tradin/module/feign/service/CognitoFeignService.java b/src/main/java/com/tradin/module/feign/service/CognitoFeignService.java deleted file mode 100644 index caa21fa..0000000 --- a/src/main/java/com/tradin/module/feign/service/CognitoFeignService.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.tradin.module.feign.service; - -import com.tradin.module.feign.client.CognitoClient; -import com.tradin.module.feign.client.CognitoJwkFeignClient; -import com.tradin.module.feign.client.dto.cognito.AuthDto; -import com.tradin.module.feign.client.dto.cognito.JwkDtos; -import com.tradin.module.feign.client.dto.cognito.ReissueAccessTokenDto; -import com.tradin.module.feign.client.dto.cognito.TokenDto; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional -@RequiredArgsConstructor -public class CognitoFeignService { - @Value("${secret.cognito-client-id}") - private String cognitoClientId; - - @Value("${secret.cognito-auth-redirect-uri}") - private String cognitoAuthRedirectUri; - - private final CognitoClient cognitoClient; - private final CognitoJwkFeignClient cognitoJwkFeignClient; - - public TokenDto getTokenFromCognito(String code) { - - AuthDto authDto = AuthDto.of("authorization_code", cognitoClientId, cognitoAuthRedirectUri, code); - - return cognitoClient.getAccessAndRefreshToken(authDto); - } - - public String reissueAccessTokenFromCognito(String refreshToken) { - ReissueAccessTokenDto reissueAccessTokenDto = new ReissueAccessTokenDto("refresh_token", - cognitoClientId, refreshToken); - - return cognitoClient.reissueRefreshToken(reissueAccessTokenDto).getAccessToken(); - } - - public JwkDtos getJwkKey() { - return cognitoJwkFeignClient.getJwks(); - } -} diff --git a/src/main/java/com/tradin/module/users/controller/dto/SignInResponseDto.java b/src/main/java/com/tradin/module/users/controller/dto/SignInResponseDto.java index 6597e18..a9d0258 100644 --- a/src/main/java/com/tradin/module/users/controller/dto/SignInResponseDto.java +++ b/src/main/java/com/tradin/module/users/controller/dto/SignInResponseDto.java @@ -1,7 +1,7 @@ package com.tradin.module.users.controller.dto; -import com.tradin.module.users.controller.dto.response.TokenResponseDto; +import com.tradin.module.auth.controller.dto.response.TokenResponseDto; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -9,6 +9,7 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter public class SignInResponseDto { + private Long id; private TokenResponseDto token; diff --git a/src/main/java/com/tradin/module/users/service/UsersService.java b/src/main/java/com/tradin/module/users/service/UsersService.java index 4d8e6cf..42bc560 100644 --- a/src/main/java/com/tradin/module/users/service/UsersService.java +++ b/src/main/java/com/tradin/module/users/service/UsersService.java @@ -1,5 +1,7 @@ package com.tradin.module.users.service; +import static com.tradin.common.exception.ExceptionType.NOT_FOUND_USER_EXCEPTION; + import com.tradin.common.exception.TradinException; import com.tradin.common.utils.SecurityUtils; import com.tradin.module.auth.service.dto.UserDataDto; @@ -10,31 +12,29 @@ import com.tradin.module.users.domain.repository.UsersRepository; import com.tradin.module.users.service.dto.ChangeMetadataDto; import com.tradin.module.users.service.dto.PingDto; +import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Optional; - -import static com.tradin.common.exception.ExceptionType.NOT_FOUND_USER_EXCEPTION; - @Service @Transactional @RequiredArgsConstructor public class UsersService implements UserDetailsService { + private final BinanceFeignService binanceFeignService; private final UsersRepository usersRepository; - public void saveUser(UserDataDto userDataDto, UserSocialType socialType) { - if (!isUserExist(userDataDto.getEmail())) { - Users users = userDataDto.toEntity(socialType); - usersRepository.save(users); - } + public Users saveOrGetUser(UserDataDto userDataDto, UserSocialType socialType) { + return usersRepository.findByEmail(userDataDto.getEmail()) + .orElseGet(() -> { + Users users = userDataDto.toEntity(socialType); + return usersRepository.save(users); + }); } - //TODO - FeignClient 실패 처리하기 public String ping(PingDto request) { binanceFeignService.getBtcusdtPositionQuantity(request.getBinanceApiKey(), request.getBinanceSecretKey()); return "pong"; @@ -62,7 +62,7 @@ private int getChangedLeverage(ChangeMetadataDto request, Users user) { public Users findById(Long id) { return usersRepository.findById(id) - .orElseThrow(() -> new TradinException(NOT_FOUND_USER_EXCEPTION)); + .orElseThrow(() -> new TradinException(NOT_FOUND_USER_EXCEPTION)); } public List findAutoTradingSubscriberByStrategyName(String name) { @@ -84,7 +84,7 @@ private Optional findByEmail(String email) { private Users findBySub(String sub) { return usersRepository.findBySub(sub) - .orElseThrow(() -> new TradinException(NOT_FOUND_USER_EXCEPTION)); + .orElseThrow(() -> new TradinException(NOT_FOUND_USER_EXCEPTION)); } @Override diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c8a794f..ae404f3 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -64,9 +64,15 @@ feign: secret: aes-secret: ${AES_SECRET_KEY} - cognito-client-id: ${COGNITO_CLIENT_ID} - cognito-auth-redirect-uri: ${COGNITO_AUTH_REDIRECT_URI} - cognito-issuer: ${COGNITO_ISSUER} swagger-username: ${SWAGGER_USERNAME} swagger-password: ${SWAGGER_PASSWORD} + jwt-secret: ${JWT_SECRET_KEY} +google: + login: + client_id: ${GOOGLE_CLIENT_ID} + code_uri: ${GOOGLE_CODE_URI} + token_uri: ${GOOGLE_TOKEN_URI} + client_secret: ${GOOGLE_CLIENT_SECRET} + redirect_uri: ${GOOGLE_REDIRECT_URI} + code_redirect_uri: ${GOOGLE_CODE_REDIRECT_URI} \ No newline at end of file