api token 소스정리

This commit is contained in:
2026-07-29 15:32:46 +09:00
parent e047865657
commit 14f96f037c
12 changed files with 151 additions and 86 deletions
Binary file not shown.
@@ -8,9 +8,7 @@ import net.jwsi.jcms.vpp.station.StationDto;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -19,62 +17,39 @@ import java.util.Map;
@RequiredArgsConstructor
public class ApiClient {
private final RestClient restClient;
private final RestClient externalApiClient;
public ApiResponse<List<StationDto>> fetchStations(String baseUrl, String token) {
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.path("/api/st/list")
.build()
.toUri();
return restClient.post()
.uri(uri)
.header("Authorization", "Bearer " + token)
public ApiResponse<List<StationDto>> fetchStations() {
return externalApiClient.post()
.uri("/api/st/list")
.body(Collections.emptyMap())
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<StationDto>>>() {});
}
public ApiResponse<List<ProviderDto>> fetchProviders(String baseUrl, String token) {
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.path("/api/provider/list")
.build()
.toUri();
return restClient.get()
.uri(uri)
.header("Authorization", "Bearer " + token)
public ApiResponse<List<ProviderDto>> fetchProviders() {
return externalApiClient.get()
.uri("/api/provider/list")
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<ProviderDto>>>() {});
}
public ApiResponse<List<ChgrResponseDto>> fetchChgr(String baseUrl, String token) {
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.path("/api/charger/list")
.build()
.toUri();
return restClient.post()
.uri(uri)
.header("Authorization", "Bearer " + token)
public ApiResponse<List<ChgrResponseDto>> fetchChgr() {
return externalApiClient.post()
.uri("/api/charger/list")
.body(Collections.emptyMap())
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrResponseDto>>>() {});
}
public ApiResponse<List<ChgrHistResponseDto>> fetchChgrHist(String baseUrl, String token) {
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.path("/api/charge/hist")
.build()
.toUri();
public ApiResponse<List<ChgrHistResponseDto>> fetchChgrHist(String startDt, String pageSize) {
Map<String, String> requestBody = Map.of(
"startDt", "2026-07-29",
"pageSize","50"
"startDt", startDt,
"pageSize", pageSize
);
return restClient.post()
.uri(uri)
.header("Authorization", "Bearer " + token)
return externalApiClient.post()
.uri("/api/charge/hist")
.body(requestBody)
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<List<ChgrHistResponseDto>>>() {});
@@ -0,0 +1,13 @@
package net.jwsi.jcms.vpp.api;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Getter
public class ApiToken {
private String jwt;
private List<String> roles;
}
@@ -0,0 +1,62 @@
package net.jwsi.jcms.vpp.api;
import net.jwsi.jcms.vpp.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.Map;
@Service
public class ExternalApiService {
private final RestClient authRestClient = RestClient.create();
@Value("${jcms.api.base-url}")
private String baseUrl;
@Value("${jcms.api.user-id}")
private String apiUserId;
@Value("${jcms.api.user-pwd}")
private String apiUserPwd;
private String cachedToken;
private LocalDateTime tokenExpiryTime;
public synchronized String getAccessToken() {
if (cachedToken != null && tokenExpiryTime != null && LocalDateTime.now().plusMinutes(1).isBefore(tokenExpiryTime)) {
return cachedToken;
}
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.path("/api/auth/token")
.build()
.toUri();
Map<String, String> requestBody = Map.of(
"userId", apiUserId,
"pwd", apiUserPwd
);
ApiResponse<ApiToken> response = authRestClient.post()
.uri(uri)
.body(requestBody)
.retrieve()
.body(new ParameterizedTypeReference<ApiResponse<ApiToken>>() {});
if (response != null && response.getData() != null) {
ApiToken tokenDto = response.getData();
this.cachedToken = tokenDto.getJwt();
this.tokenExpiryTime = JwtUtil.getExpirationTime(this.cachedToken);
return this.cachedToken;
}
throw new IllegalStateException("외부 API 토큰 발급에 실패했습니다.");
}
}
@@ -1,23 +1,25 @@
package net.jwsi.jcms.vpp.api;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
import java.time.Duration;
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout((int) Duration.ofSeconds(5).toMillis());
factory.setReadTimeout((int) Duration.ofSeconds(30).toMillis());
@Value("${jcms.api.base-url}")
private String baseUrl;
@Bean
public RestClient externalApiClient(ExternalApiService externalApiService) {
return RestClient.builder()
.requestFactory(factory)
.baseUrl(baseUrl)
.requestInterceptor((request, body, execution) -> {
String token = externalApiService.getAccessToken();
request.getHeaders().set("Authorization", "Bearer " + token);
return execution.execute(request, body);
})
.build();
}
}
@@ -8,7 +8,6 @@ import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
@@ -22,11 +21,6 @@ import java.util.stream.Collectors;
public class ChgrService extends BaseService<Chgr,String,ChgrRepository> {
private final ApiClient apiClient;
@Value("${jcms.api.base-url}")
private String baseUrl;
@Value("${jcms.api.token}")
private String apiToken;
public ChgrService(ChgrRepository repository, ApiClient apiClient) {
super(repository);
this.apiClient = apiClient;
@@ -34,7 +28,7 @@ public class ChgrService extends BaseService<Chgr,String,ChgrRepository> {
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void getCharger() {
ApiResponse<List<ChgrResponseDto>> response = apiClient.fetchChgr(baseUrl, apiToken);
ApiResponse<List<ChgrResponseDto>> response = apiClient.fetchChgr();
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음";
@@ -40,7 +40,7 @@ public class ChgrHist extends BaseStEntity {
@Column(name = "RECHG_TIME")
private String rechgTime; // 충전시간
@Column(name = "RECH_GWH")
@Column(name = "RECHG_WH")
private Integer rechGwh; // 충전량
@Column(name = "RECHG_AMT")
@@ -8,12 +8,12 @@ import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -23,11 +23,6 @@ public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepo
private final ApiClient apiClient;
@Value("${jcms.api.base-url}")
private String baseUrl;
@Value("${jcms.api.token}")
private String apiToken;
public ChgrHistService(ChgrHistRepository repository, ApiClient apiClient) {
super(repository);
@@ -37,7 +32,8 @@ public class ChgrHistService extends BaseService<ChgrHist, Integer, ChgrHistRepo
@Transactional
@Scheduled(cron = "0 */10 * * * *")
public void getChgrHist() throws IllegalAccessException {
ApiResponse<List<ChgrHistResponseDto>> response = apiClient.fetchChgrHist(baseUrl, apiToken);
ApiResponse<List<ChgrHistResponseDto>> response = apiClient.fetchChgrHist(LocalDateTime.now().toString(), "50");
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음";
@@ -8,15 +8,11 @@ import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.api.ApiClient;
import net.jwsi.jcms.vpp.api.ApiResponse;
import net.jwsi.jcms.vpp.station.Station;
import net.jwsi.jcms.vpp.station.StationDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -26,12 +22,6 @@ public class ProviderService extends BaseService<Provider, String, ProviderRepos
private final ApiClient apiClient;
@Value("${jcms.api.base-url}")
private String baseUrl;
@Value("${jcms.api.token}")
private String apiToken;
public ProviderService(ProviderRepository repository, ApiClient apiClient) {
super(repository);
this.apiClient = apiClient;
@@ -40,7 +30,7 @@ public class ProviderService extends BaseService<Provider, String, ProviderRepos
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void getProviders() {
ApiResponse<List<ProviderDto>> response = apiClient.fetchProviders(baseUrl, apiToken);
ApiResponse<List<ProviderDto>> response = apiClient.fetchProviders();
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음";
@@ -6,15 +6,13 @@ import net.jwsi.jcms.base.BaseService;
import net.jwsi.jcms.base.EnumSqlType;
import net.jwsi.jcms.utils.HttpUtil;
import net.jwsi.jcms.utils.LoginUtil;
import net.jwsi.jcms.vpp.api.ApiResponse;
import net.jwsi.jcms.vpp.api.ApiClient;
import org.springframework.beans.factory.annotation.Value;
import net.jwsi.jcms.vpp.api.ApiResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -23,11 +21,7 @@ import java.util.stream.Collectors;
public class StationService extends BaseService<Station,String,StationRepository> {
private final ApiClient apiClient;
@Value("${jcms.api.base-url}")
private String baseUrl;
@Value("${jcms.api.token}")
private String apiToken;
public StationService(StationRepository repository, ApiClient apiClient) {
super(repository);
this.apiClient = apiClient;
@@ -36,7 +30,7 @@ public class StationService extends BaseService<Station,String,StationRepository
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void getStations() {
ApiResponse<List<StationDto>> response = apiClient.fetchStations(baseUrl, apiToken);
ApiResponse<List<StationDto>> response = apiClient.fetchStations();
if (response == null || !Integer.valueOf(200).equals(response.getCode())) {
String msg = (response != null) ? response.getMessage() : "응답 없음";
@@ -0,0 +1,38 @@
package net.jwsi.jcms.vpp.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
public class JwtUtil {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static LocalDateTime getExpirationTime(String jwtToken) {
try {
String[] parts = jwtToken.split("\\.");
if (parts.length < 2) {
throw new IllegalArgumentException("올바르지 않은 JWT 토큰 형식입니다.");
}
String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);
JsonNode payloadNode = objectMapper.readTree(payloadJson);
if (!payloadNode.has("exp")) {
throw new IllegalStateException("토큰에 exp(만료시간) 필드가 존재하지 않습니다.");
}
long expSeconds = payloadNode.get("exp").asLong();
return LocalDateTime.ofInstant(Instant.ofEpochSecond(expSeconds), ZoneId.systemDefault());
} catch (Exception e) {
throw new RuntimeException("토큰 만료 시간 추출 실패", e);
}
}
}
+4 -3
View File
@@ -19,12 +19,12 @@ jcms:
manager-path: cms
attr:
main-url: /cms/main
new-user-st: 9 # 1: ??, 9:????
new-user-st: 9
cms-name: VPP
cms-title: VPP
cms-bg-color: '#499'
cms-image-url: /public/images/logo_jcms3.png
copyright: Copyright 2026 ??????????? Inc.
copyright: Copyright 2026 주식회사 진우시스템 Inc.
authenticated-patten: /cms/**, /system/**, /api/**
pass-url: /cms/main
public-url: ''
@@ -33,4 +33,5 @@ jcms:
days: 30
api:
base-url: ${API_BASE_URL:http://dev.jinwoosi.co.kr:8072}
token: ${API_TOKEN}
user-id: ${API_USER_ID:apiuser}
user-pwd: ${API_USER_PWD:apiuser}