반응형
먼저 개발환경을 보겠습니다.
1. spring boot 3.1.1
2. httpClient 5
이 코드는 Spring Framework에서 RestTemplate을 사용하기 위한 구성 파일인 'RestTemplateConfig' 클래스를 정의하고 있습니다. RestTemplate은 HTTP 통신을 쉽게 수행할 수 있는 Spring의 클래스입니다. 아래는 코드의 각 부분을 설명합니다.
- '@Configuration': 이 어노테이션은 이 클래스가 Spring의 구성 파일임을 나타냅니다. 이 클래스에서 Bean을 정의하고 구성합니다.
- '@Bean' 어노테이션: 이 어노테이션은 해당 메서드가 Spring 컨테이너에 Bean을 생성하고 등록하는 메서드임을 나타냅니다.
- 'httpClient()' 메서드: 이 메서드는 HttpClient를 생성하고 구성합니다. HttpClient는 HTTP 요청을 수행하는 데 사용됩니다. 여기서 SSL 인증서 검증을 무시하도록 구성되며, 모든 인증서를 신뢰합니다. 이는 보안적으로 권장되지 않을 수 있으므로 주의해야 합니다. 또한 Https 인증 요청시 호스트네임 유효성 검사를 수행하지 않도록 설정합니다.
- 'factory(HttpClient httpClient)' 메서드: 이 메서드는 'HttpComponentsClientHttpRequestFactory' 를 생성하고 구성합니다. 이 Factory는 RestTemplate이 HTTP 클라이언트 요청을 보낼 때 사용하는 기본 설정을 구성합니다. 여기서는 연결 타임아웃을 3,000 밀리초로 설정하고, 위에서 생성한 'httpClient' 를 사용합니다.
- 'restTemplate(HttpComponentsClientHttpRequestFactory factory)' 메서드: 이 메서드는 'RestTemplate' 을 생성합니다. 'HttpComponentsClientHttpRequestFactory' 를 사용하여 RestTemplate이 HTTP 요청을 수행하도록 설정합니다. 이렇게 생성된 RestTemplate은 다양한 HTTP 요청을 수행하는 데 사용할 수 있습니다.
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
HttpClient httpClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
// 모든 인증서를 신뢰하도록 설정한다
TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
// Https 인증 요청시 호스트네임 유효성 검사를 진행하지 않게 한다.
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory()).build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(connectionManager);
return httpClientBuilder.build();
}
@Bean
HttpComponentsClientHttpRequestFactory factory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(3000);
factory.setHttpClient(httpClient);
return factory;
}
@Bean
RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
}
위에 설정한 RestTemplate Bean을 주입받는 Util Class를 생성합니다.
- @Autowired 어노테이션: RestTemplate 객체를 자동으로 주입 받습니다. 이 객체는 HTTP 요청을 수행하기 위해 사용됩니다.
- postForObject 메서드: 이 메서드는 POST 요청을 보내고 응답을 처리하는 주요 로직을 포함합니다. 메서드 시그니처는 다음과 같습니다.
- url: 요청을 보낼 URL
- queryParams: HTTP 요청의 쿼리 매개변수 (POST 요청 본문 데이터)
- responseType: 요청을 통해 받을 응답 데이터의 타입을 나타내는 ParameterizedTypeReference
- jwt: JWT (JSON Web Token) 인증 토큰
- UriComponentsBuilder: url을 기반으로 URI를 생성하기 위해 사용됩니다.
- HttpHeaders: HTTP 요청 헤더를 설정합니다. 이 코드에서는 Content-Type을 APPLICATION_FORM_URLENCODED로 설정하고, Bearer 토큰을 포함하는 인증 헤더를 설정합니다.
- restTemplate.exchange(): RestTemplate을 사용하여 HTTP 요청을 보내고 응답을 받습니다. uri.toUri()를 사용하여 URI로 변환하고, HttpMethod.POST를 사용하여 POST 요청을 보냅니다. 요청 헤더와 요청 본문 데이터를 포함하여 요청을 수행하고, responseType에 지정된 타입으로 응답 데이터를 받아옵니다.
- 예외 처리: 예외가 발생할 경우, 로그를 기록하고 에러 메시지를 응답으로 반환합니다. 주석 처리된 부분은 예외 발생 시 다른 처리 방법을 시도하려는 시도였을 것으로 보입니다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class HttpUtil<T> {
@Autowired
private RestTemplate restTemplate;
public T postForObject(String url, MultiValueMap<String, String> queryParams, ParameterizedTypeReference<T> responseType, String jwt) {
T response = null;
try {
UriComponents uri = UriComponentsBuilder
.fromUriString(url)
.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setBearerAuth(jwt);
ResponseEntity<T> responseEntity = restTemplate.exchange(
uri.toUri(),
HttpMethod.POST,
new HttpEntity<>(queryParams, headers),
responseType);
response = responseEntity.getBody();
} catch (Exception e) {
log.error("err: {}", e.getMessage());
String err = e.getMessage();
response = (T) err;
}
return response;
}
}
HttpUtil을 주입받아 JWT을 인증하는 코드 중 일부입니다.
이 코드는 JWT (JSON Web Token) 관리 및 로그인 프로세스를 처리하는 JwtTokenManager 클래스를 정의하고 있습니다. 이 클래스는 Spring의 @Component 어노테이션으로 Spring Bean으로 등록되어 관리되며, HttpUtil 클래스를 사용하여 HTTP 요청을 수행합니다. 아래는 코드의 주요 부분을 설명합니다
- apiUrl, queryParams, token 매개변수: API 엔드포인트 URL, 쿼리 매개변수, JWT 토큰을 입력으로 받습니다.
- ResultVo<ResultResponse, JwtTokenDto>: 이 부분은 요청 결과를 저장합니다.
- httpUtil.postForObject(url, queryParams, responseType, token): httpUtil 객체를 사용하여 HTTP POST 요청을 보냅니다. 이 요청은 url에 지정된 API 엔드포인트로 가고, queryParams는 POST 요청 본문 데이터로 전달됩니다. 요청을 보낼 때 token도 함께 전송됩니다. 이 때, 응답은 문자열로 받아옵니다.
- JSON 파싱: 받아온 문자열 응답을 JSON 형식으로 파싱합니다. ObjectMapper를 사용하여 JSON 문자열을 ResultVo<ResultResponse, JwtTokenDto> 객체로 변환합니다.
- resultVo 확인: 응답 데이터를 파싱한 resultVo 객체를 확인하여 로그인 성공 여부를 판단합니다. resultVo의 getResult().getCode()가 Const.LOGIN_SUCCESS_CODE와 일치하면 로그인이 성공한 것으로 간주합니다.
- 로그인 성공 시: 로그인이 성공하면 JWT 토큰에서 얻은 정보를 사용하여 Spring Security의 Authentication 객체를 생성하고, 이를 현재 실행 중인 스레드의 SecurityContextHolder에 설정합니다. 이렇게 하면 사용자가 인증되었음을 시스템에 알립니다.
- 로그인 실패 시: 로그인이 실패하면 BadCredentialsException을 던져서 로그인 실패를 처리합니다.
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtTokenManager {
private final HttpUtil<String> httpUtil;
@Value("${gateway.url}")
private String gatewaySvr;
public Authentication jwtLoginProcess(String apiUrl, MultiValueMap<String, String> queryParams, String token) {
//토큰 만료시
ResultVo<ResultResponse, JwtTokenDto> resultVo = null;
try {
String url = "http://" + gatewaySvr + "/conn" + apiUrl;
/*
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
queryParams.add("id", 'test');
queryParams.add("password", 'test@123');
*/
ParameterizedTypeReference<String> responseType = new ParameterizedTypeReference<>() {};
String res = httpUtil.postForObject(url, queryParams, responseType, token);
ObjectMapper mapper = new ObjectMapper();
resultVo = mapper.readValue(res, new TypeReference<ResultVo<ResultResponse, JwtTokenDto>>() {});
} catch (Exception ex) {
ex.printStackTrace();
log.error("err : {}", ex.getMessage());
}
if ( resultVo != null
&& Const.LOGIN_SUCCESS_CODE.equals(resultVo.getResult().getCode() ) ) {
JwtTokenDto jwtTokenDto = resultVo.getData();
Authentication newAuth = getAuthentication(jwtTokenDto);
SecurityContextHolder.getContext().setAuthentication(newAuth);
return newAuth;
} else {
// 로그인실패시 (아이디,비밀번호 오류 등)
throw new BadCredentialsException(resultVo.getResult().getMessage());
}
}
}
반응형
'Programing > Java & Spring' 카테고리의 다른 글
WebFlux, WebClient 사용하여 API 호출 - 2편 (0) | 2023.09.26 |
---|---|
WebFlux, WebClient 사용하여 API 호출 - 1편 (0) | 2023.09.26 |
함수형 인터페이스(Funcational Interface) (0) | 2023.09.22 |
스프링(Spring) 에서 RequestURI, RequestURL 차이 (0) | 2023.09.08 |
Java Default Method (디펄트 메소드) (0) | 2023.09.08 |
AOP(Aspect-Oriented Programming) 개념 및 예시 (0) | 2023.09.08 |
[JAVA] content-disposition (0) | 2013.12.31 |
[SPRING3.0] 메세지 처리 (0) | 2013.09.12 |
[SPRING3.0] 페이스북 공유하기① (0) | 2013.09.11 |
[SPRING3.0] 데이터 엑세스 기술② (0) | 2013.09.04 |