mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-01-21 13:46:26 +00:00
feat: jwt 토큰 비활성화 및 회의예약API 개발
This commit is contained in:
parent
d9261bad2c
commit
6e7b910a8d
File diff suppressed because it is too large
Load Diff
1
meeting/logs/meeting.log
Normal file
1
meeting/logs/meeting.log
Normal file
@ -0,0 +1 @@
|
||||
nohup: ./gradlew: No such file or directory
|
||||
@ -37,6 +37,16 @@ public class Meeting {
|
||||
*/
|
||||
private LocalDateTime scheduledAt;
|
||||
|
||||
/**
|
||||
* 회의 종료 예정 일시
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 회의 장소
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 회의 시작 일시
|
||||
*/
|
||||
|
||||
@ -78,8 +78,8 @@ public class MeetingDTO {
|
||||
.meetingId(meeting.getMeetingId())
|
||||
.title(meeting.getTitle())
|
||||
.startTime(meeting.getStartedAt() != null ? meeting.getStartedAt() : meeting.getScheduledAt())
|
||||
.endTime(meeting.getEndedAt())
|
||||
.location(null) // Meeting 도메인에 location 필드가 없어서 null로 설정
|
||||
.endTime(meeting.getEndedAt() != null ? meeting.getEndedAt() : meeting.getEndTime())
|
||||
.location(meeting.getLocation())
|
||||
.agenda(meeting.getDescription())
|
||||
.participants(meeting.getParticipants().stream()
|
||||
.map(participantId -> ParticipantDTO.builder()
|
||||
|
||||
@ -49,6 +49,8 @@ public class MeetingService implements
|
||||
.title(command.title())
|
||||
.description(command.description())
|
||||
.scheduledAt(command.scheduledAt())
|
||||
.endTime(command.endTime())
|
||||
.location(command.location())
|
||||
.status("SCHEDULED")
|
||||
.organizerId(command.organizerId())
|
||||
.participants(command.participants())
|
||||
|
||||
@ -22,6 +22,8 @@ public interface CreateMeetingUseCase {
|
||||
String title,
|
||||
String description,
|
||||
LocalDateTime scheduledAt,
|
||||
LocalDateTime endTime,
|
||||
String location,
|
||||
String organizerId,
|
||||
List<String> participants,
|
||||
String templateId
|
||||
|
||||
@ -12,11 +12,15 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Spring Security 설정
|
||||
@ -48,7 +52,8 @@ public class SecurityConfig {
|
||||
// WebSocket endpoints
|
||||
.requestMatchers("/ws/**").permitAll()
|
||||
// All other requests require authentication
|
||||
.anyRequest().authenticated()
|
||||
// .anyRequest().authenticated()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
|
||||
UsernamePasswordAuthenticationFilter.class)
|
||||
@ -69,7 +74,8 @@ public class SecurityConfig {
|
||||
// 허용할 헤더
|
||||
configuration.setAllowedHeaders(Arrays.asList(
|
||||
"Authorization", "Content-Type", "X-Requested-With", "Accept",
|
||||
"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"
|
||||
"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers",
|
||||
"X-User-Id", "X-User-Name", "X-User-Email"
|
||||
));
|
||||
|
||||
// 자격 증명 허용
|
||||
@ -82,4 +88,24 @@ public class SecurityConfig {
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* HttpFirewall 설정
|
||||
* 한글을 포함한 모든 문자를 헤더 값으로 허용
|
||||
*/
|
||||
@Bean
|
||||
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
|
||||
StrictHttpFirewall firewall = new StrictHttpFirewall();
|
||||
|
||||
// 한글을 포함한 모든 문자를 허용하도록 설정
|
||||
firewall.setAllowedHeaderValues(header -> true);
|
||||
|
||||
// URL 인코딩된 슬래시 허용
|
||||
firewall.setAllowUrlEncodedSlash(true);
|
||||
|
||||
// 세미콜론 허용
|
||||
firewall.setAllowSemicolon(true);
|
||||
|
||||
return firewall;
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,36 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
// 1. X-User-* 헤더를 통한 인증 (개발/테스트용)
|
||||
String headerUserId = request.getHeader("X-User-Id");
|
||||
String headerUserName = request.getHeader("X-User-Name");
|
||||
String headerUserEmail = request.getHeader("X-User-Email");
|
||||
|
||||
if (StringUtils.hasText(headerUserId)) {
|
||||
// X-User-* 헤더가 있으면 이를 사용하여 인증
|
||||
UserPrincipal userPrincipal = UserPrincipal.builder()
|
||||
.userId(headerUserId)
|
||||
.username(headerUserName != null ? headerUserName : "unknown")
|
||||
.email(headerUserEmail)
|
||||
.authority("USER")
|
||||
.build();
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
userPrincipal,
|
||||
null,
|
||||
Collections.singletonList(new SimpleGrantedAuthority("USER"))
|
||||
);
|
||||
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
log.debug("헤더 기반 인증된 사용자: {} ({})", userPrincipal.getUsername(), headerUserId);
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. JWT 토큰을 통한 인증
|
||||
String token = jwtTokenProvider.resolveToken(request);
|
||||
|
||||
if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) {
|
||||
@ -69,7 +99,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
log.debug("인증된 사용자: {} ({})", userPrincipal.getUsername(), userId);
|
||||
log.debug("JWT 기반 인증된 사용자: {} ({})", userPrincipal.getUsername(), userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,11 @@ public class UserPrincipal {
|
||||
*/
|
||||
private final String username;
|
||||
|
||||
/**
|
||||
* 사용자 이메일
|
||||
*/
|
||||
private final String email;
|
||||
|
||||
/**
|
||||
* 사용자 권한
|
||||
*/
|
||||
|
||||
@ -65,6 +65,8 @@ public class MeetingController {
|
||||
request.getTitle(),
|
||||
request.getAgenda(),
|
||||
request.getStartTime(),
|
||||
request.getEndTime(),
|
||||
request.getLocation(),
|
||||
userId,
|
||||
request.getParticipants(),
|
||||
null // 템플릿 ID는 나중에 적용
|
||||
|
||||
@ -37,6 +37,12 @@ public class MeetingEntity extends BaseTimeEntity {
|
||||
@Column(name = "scheduled_at", nullable = false)
|
||||
private LocalDateTime scheduledAt;
|
||||
|
||||
@Column(name = "end_time")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Column(name = "location", length = 200)
|
||||
private String location;
|
||||
|
||||
@Column(name = "started_at")
|
||||
private LocalDateTime startedAt;
|
||||
|
||||
@ -62,6 +68,8 @@ public class MeetingEntity extends BaseTimeEntity {
|
||||
.title(this.title)
|
||||
.description(this.description)
|
||||
.scheduledAt(this.scheduledAt)
|
||||
.endTime(this.endTime)
|
||||
.location(this.location)
|
||||
.startedAt(this.startedAt)
|
||||
.endedAt(this.endedAt)
|
||||
.status(this.status)
|
||||
@ -77,6 +85,8 @@ public class MeetingEntity extends BaseTimeEntity {
|
||||
.title(meeting.getTitle())
|
||||
.description(meeting.getDescription())
|
||||
.scheduledAt(meeting.getScheduledAt())
|
||||
.endTime(meeting.getEndTime())
|
||||
.location(meeting.getLocation())
|
||||
.startedAt(meeting.getStartedAt())
|
||||
.endedAt(meeting.getEndedAt())
|
||||
.status(meeting.getStatus())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user