Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/main/java/server/loop/global/config/WebSocketConfig.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
package server.loop.global.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.*;
import server.loop.global.config.web.StompAuthChannelInterceptor;

@RequiredArgsConstructor
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

private final StompAuthChannelInterceptor stompAuthChannelInterceptor;

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(stompAuthChannelInterceptor);
}

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user"); // ★ 유저 개별 대상
config.setUserDestinationPrefix("/user");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // 클라이언트 연결 주소
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package server.loop.global.config.web;

import lombok.RequiredArgsConstructor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import server.loop.global.security.CustomUserDetailsService;
import server.loop.global.security.JwtTokenProvider;

@Component
@RequiredArgsConstructor
public class StompAuthChannelInterceptor implements ChannelInterceptor {

private final JwtTokenProvider jwtTokenProvider;
private final CustomUserDetailsService userDetailsService;

@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String authHeader = accessor.getFirstNativeHeader("Authorization");

if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String email = jwtTokenProvider.getEmailFromToken(token);

UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

accessor.setUser(authentication); // ★ 중요: SecurityContext가 아닌 accessor에 설정
}
}

return message;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public String getEmail(String token) {
.getSubject();
}

public String getEmailFromToken(String token) {
return getEmail(token);
}
// Token 유효성 검증
public boolean validateToken(String token) {
try {
Expand Down