mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-06-13 04:49:11 +00:00
user service 빌드 성공
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.unicorn.hgzero.user.config;
|
||||
|
||||
import com.azure.messaging.eventhubs.EventHubProducerClient;
|
||||
import com.azure.messaging.eventhubs.EventHubClientBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Azure EventHub 설정
|
||||
* 사용자 인증 관련 이벤트 발행을 위한 EventHub 설정
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class EventHubConfig {
|
||||
|
||||
@Value("${spring.cloud.azure.eventhub.connection-string:}")
|
||||
private String connectionString;
|
||||
|
||||
@Value("${spring.cloud.azure.eventhub.name:hgzero-eventhub-name}")
|
||||
private String eventHubName;
|
||||
|
||||
/**
|
||||
* EventHub Producer Client 빈 생성
|
||||
* 연결 문자열이 있을 때만 생성
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnExpression("'${spring.cloud.azure.eventhub.connection-string:}'.length() > 0")
|
||||
public EventHubProducerClient eventHubProducerClient() {
|
||||
try {
|
||||
EventHubProducerClient client = new EventHubClientBuilder()
|
||||
.connectionString(connectionString, eventHubName)
|
||||
.buildProducerClient();
|
||||
|
||||
log.info("EventHub Producer Client 생성 완료: {}", eventHubName);
|
||||
return client;
|
||||
} catch (Exception e) {
|
||||
log.error("EventHub Producer Client 생성 실패: {}", e.getMessage());
|
||||
throw new RuntimeException("EventHub Producer Client 생성 실패", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class JwtTokenProvider {
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token);
|
||||
return true;
|
||||
@@ -67,7 +67,7 @@ public class JwtTokenProvider {
|
||||
*/
|
||||
public String getUserId(String token) {
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
@@ -80,7 +80,7 @@ public class JwtTokenProvider {
|
||||
*/
|
||||
public String getUsername(String token) {
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
@@ -93,7 +93,7 @@ public class JwtTokenProvider {
|
||||
*/
|
||||
public String getAuthority(String token) {
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
@@ -107,7 +107,7 @@ public class JwtTokenProvider {
|
||||
public boolean isTokenExpired(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
@@ -123,7 +123,7 @@ public class JwtTokenProvider {
|
||||
*/
|
||||
public Date getExpirationDate(String token) {
|
||||
Claims claims = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.unicorn.hgzero.user.service;
|
||||
|
||||
/**
|
||||
* 이벤트 발행 서비스 인터페이스
|
||||
* 사용자 인증 관련 이벤트를 외부 시스템에 발행
|
||||
*/
|
||||
public interface EventPublishService {
|
||||
|
||||
/**
|
||||
* 로그인 이벤트 발행
|
||||
*
|
||||
* @param userId 사용자 ID
|
||||
* @param username 사용자명
|
||||
* @param timestamp 로그인 시각
|
||||
*/
|
||||
void publishLoginEvent(String userId, String username, long timestamp);
|
||||
|
||||
/**
|
||||
* 로그아웃 이벤트 발행
|
||||
*
|
||||
* @param userId 사용자 ID
|
||||
* @param timestamp 로그아웃 시각
|
||||
*/
|
||||
void publishLogoutEvent(String userId, long timestamp);
|
||||
|
||||
/**
|
||||
* 토큰 갱신 이벤트 발행
|
||||
*
|
||||
* @param userId 사용자 ID
|
||||
* @param timestamp 토큰 갱신 시각
|
||||
*/
|
||||
void publishTokenRefreshEvent(String userId, long timestamp);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.unicorn.hgzero.user.service;
|
||||
|
||||
import com.azure.messaging.eventhubs.EventData;
|
||||
import com.azure.messaging.eventhubs.EventHubProducerClient;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 이벤트 발행 서비스 구현체
|
||||
* Azure EventHub를 통한 사용자 인증 이벤트 발행
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(EventHubProducerClient.class)
|
||||
public class EventPublishServiceImpl implements EventPublishService {
|
||||
|
||||
private final EventHubProducerClient eventHubProducerClient;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void publishLoginEvent(String userId, String username, long timestamp) {
|
||||
Map<String, Object> eventData = new HashMap<>();
|
||||
eventData.put("eventType", "USER_LOGIN");
|
||||
eventData.put("userId", userId);
|
||||
eventData.put("username", username);
|
||||
eventData.put("timestamp", timestamp);
|
||||
eventData.put("eventTime", Instant.ofEpochMilli(timestamp).toString());
|
||||
|
||||
publishEvent(eventData, "로그인 이벤트");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishLogoutEvent(String userId, long timestamp) {
|
||||
Map<String, Object> eventData = new HashMap<>();
|
||||
eventData.put("eventType", "USER_LOGOUT");
|
||||
eventData.put("userId", userId);
|
||||
eventData.put("timestamp", timestamp);
|
||||
eventData.put("eventTime", Instant.ofEpochMilli(timestamp).toString());
|
||||
|
||||
publishEvent(eventData, "로그아웃 이벤트");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishTokenRefreshEvent(String userId, long timestamp) {
|
||||
Map<String, Object> eventData = new HashMap<>();
|
||||
eventData.put("eventType", "TOKEN_REFRESH");
|
||||
eventData.put("userId", userId);
|
||||
eventData.put("timestamp", timestamp);
|
||||
eventData.put("eventTime", Instant.ofEpochMilli(timestamp).toString());
|
||||
|
||||
publishEvent(eventData, "토큰 갱신 이벤트");
|
||||
}
|
||||
|
||||
/**
|
||||
* EventHub로 이벤트 발행
|
||||
*
|
||||
* @param eventData 이벤트 데이터
|
||||
* @param eventDescription 이벤트 설명 (로깅용)
|
||||
*/
|
||||
private void publishEvent(Map<String, Object> eventData, String eventDescription) {
|
||||
if (eventHubProducerClient == null) {
|
||||
log.debug("EventHub Producer Client가 없어 {} 발행을 건너뜀", eventDescription);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String jsonData = objectMapper.writeValueAsString(eventData);
|
||||
EventData event = new EventData(jsonData);
|
||||
|
||||
// EventData를 Iterable로 감싸서 전송
|
||||
eventHubProducerClient.send(java.util.Collections.singletonList(event));
|
||||
log.debug("{} 발행 완료: {}", eventDescription, eventData.get("userId"));
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("{} JSON 변환 실패: {}", eventDescription, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("{} 발행 실패: {}", eventDescription, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.unicorn.hgzero.user.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* No-Op 이벤트 발행 서비스
|
||||
* EventHub가 설정되지 않은 경우 사용되는 기본 구현체
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ConditionalOnMissingBean(EventPublishServiceImpl.class)
|
||||
public class NoOpEventPublishService implements EventPublishService {
|
||||
|
||||
@Override
|
||||
public void publishLoginEvent(String userId, String username, long timestamp) {
|
||||
log.debug("EventHub 미설정으로 로그인 이벤트 발행 건너뜀: userId={}", userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishLogoutEvent(String userId, long timestamp) {
|
||||
log.debug("EventHub 미설정으로 로그아웃 이벤트 발행 건너뜀: userId={}", userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishTokenRefreshEvent(String userId, long timestamp) {
|
||||
log.debug("EventHub 미설정으로 토큰 갱신 이벤트 발행 건너뜀: userId={}", userId);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ public class UserServiceImpl implements UserService {
|
||||
private final UserRepository userRepository;
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
private final EventPublishService eventPublishService;
|
||||
|
||||
@Value("${jwt.secret}")
|
||||
private String jwtSecret;
|
||||
@@ -123,6 +124,9 @@ public class UserServiceImpl implements UserService {
|
||||
saveRefreshToken(user.getUserId(), refreshToken);
|
||||
|
||||
log.info("로그인 성공: userId={}", request.getUserId());
|
||||
|
||||
// 로그인 이벤트 발행
|
||||
eventPublishService.publishLoginEvent(user.getUserId(), user.getUsername(), System.currentTimeMillis());
|
||||
|
||||
return LoginResponse.builder()
|
||||
.accessToken(accessToken)
|
||||
@@ -173,6 +177,9 @@ public class UserServiceImpl implements UserService {
|
||||
String newAccessToken = generateAccessToken(user);
|
||||
|
||||
log.info("토큰 갱신 성공: userId={}", userId);
|
||||
|
||||
// 토큰 갱신 이벤트 발행
|
||||
eventPublishService.publishTokenRefreshEvent(userId, System.currentTimeMillis());
|
||||
|
||||
return RefreshTokenResponse.builder()
|
||||
.accessToken(newAccessToken)
|
||||
@@ -191,6 +198,9 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
// Redis에서 Refresh Token 삭제
|
||||
deleteRefreshToken(userId);
|
||||
|
||||
// 로그아웃 이벤트 발행
|
||||
eventPublishService.publishLogoutEvent(userId, System.currentTimeMillis());
|
||||
|
||||
log.info("로그아웃 완료: userId={}", userId);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:userdb}
|
||||
username: ${DB_USERNAME:hgzerouser}
|
||||
password: ${DB_PASSWORD:}
|
||||
password: ${DB_PASSWORD:Hi5Jessica!}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 20
|
||||
@@ -25,8 +25,13 @@ spring:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
use_sql_comments: true
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
jdbc:
|
||||
time_zone: UTC
|
||||
hibernate:
|
||||
ddl-auto: ${JPA_DDL_AUTO:update}
|
||||
database: postgresql
|
||||
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||
|
||||
# Redis Configuration
|
||||
data:
|
||||
@@ -43,13 +48,13 @@ spring:
|
||||
max-wait: -1ms
|
||||
database: ${REDIS_DATABASE:0}
|
||||
|
||||
# LDAP Configuration
|
||||
ldap:
|
||||
urls: ${LDAP_URLS:ldaps://ldap.example.com:636}
|
||||
base: ${LDAP_BASE:dc=example,dc=com}
|
||||
username: ${LDAP_USERNAME:}
|
||||
password: ${LDAP_PASSWORD:}
|
||||
user-dn-pattern: ${LDAP_USER_DN_PATTERN:uid={0},ou=people}
|
||||
# LDAP Configuration (비활성화 for development)
|
||||
# ldap:
|
||||
# urls: ${LDAP_URLS:ldaps://ldap.example.com:636}
|
||||
# base: ${LDAP_BASE:dc=example,dc=com}
|
||||
# username: ${LDAP_USERNAME:}
|
||||
# password: ${LDAP_PASSWORD:}
|
||||
# user-dn-pattern: ${LDAP_USER_DN_PATTERN:uid={0},ou=people}
|
||||
|
||||
# Event Configuration (Azure EventHub)
|
||||
cloud:
|
||||
@@ -96,6 +101,8 @@ management:
|
||||
enabled: true
|
||||
readinessState:
|
||||
enabled: true
|
||||
ldap:
|
||||
enabled: false
|
||||
|
||||
# OpenAPI Documentation
|
||||
springdoc:
|
||||
|
||||
Reference in New Issue
Block a user