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
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.sofa.linkiving.global.config;

import java.util.List;
import java.util.Map;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;

import com.sofa.linkiving.security.config.StompHandler;
import com.sofa.linkiving.security.resolver.AuthMemberWebsocketArgumentResolver;

import jakarta.servlet.http.Cookie;
import lombok.RequiredArgsConstructor;

@Configuration
Expand All @@ -33,12 +40,38 @@ public void configureMessageBroker(MessageBrokerRegistry config) {

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
HandshakeInterceptor cookieInterceptor = new HandshakeInterceptor() {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) {
if (request instanceof ServletServerHttpRequest servletRequest) {
Cookie[] cookies = servletRequest.getServletRequest().getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("accessToken".equals(cookie.getName())) {
attributes.put("accessToken", cookie.getValue()); // 세션 주머니에 보관!
break;
}
}
}
}
return true;
}

@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
};

registry.addEndpoint("/ws/chat")
.setAllowedOriginPatterns("*")
.addInterceptors(cookieInterceptor)
.withSockJS();

registry.addEndpoint("/ws/link")
.setAllowedOriginPatterns("*")
.addInterceptors(cookieInterceptor)
.withSockJS();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ public abstract class SecurityConstants {
/* h2 */
"/h2-console/**",

/* web socket */
"/ws/chat/**",
"/ws/link/**",

/* temp */
"/v1/member/signup", "/v1/member/login", "/mock/**",

Expand Down
58 changes: 41 additions & 17 deletions src/main/java/com/sofa/linkiving/security/config/StompHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.sofa.linkiving.security.config;

import java.util.Map;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.messaging.Message;
Expand All @@ -18,7 +20,9 @@
import com.sofa.linkiving.security.jwt.error.JwtErrorCode;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
Expand All @@ -30,30 +34,50 @@ public class StompHandler implements ChannelInterceptor {
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {

String authorizationHeader = accessor.getFirstNativeHeader(JwtKeys.Headers.AUTHORIZATION);
String token = null;
if (accessor != null) {
StompCommand command = accessor.getCommand();

if (authorizationHeader != null && authorizationHeader.startsWith(JwtKeys.Headers.BEARER_PREFIX)) {
token = authorizationHeader.substring(JwtKeys.Headers.BEARER_PREFIX.length());
}
if (StompCommand.CONNECT.equals(command) || StompCommand.SEND.equals(command)) {
String token = null;
Map<String, Object> sessionAttributes = accessor.getSessionAttributes();

try {
if (token == null) {
throw new BusinessException(JwtErrorCode.EMPTY_TOKEN);
if (sessionAttributes != null && sessionAttributes.containsKey("accessToken")) {
token = (String)sessionAttributes.get("accessToken");
}

if (jwtTokenProvider.validateAccessToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
accessor.setUser(authentication);
if (token == null && StompCommand.CONNECT.equals(command)) {
String authorizationHeader = accessor.getFirstNativeHeader(JwtKeys.Headers.AUTHORIZATION);

if (authorizationHeader != null && authorizationHeader.startsWith(JwtKeys.Headers.BEARER_PREFIX)) {
token = authorizationHeader.substring(JwtKeys.Headers.BEARER_PREFIX.length());

if (sessionAttributes != null) {
sessionAttributes.put("accessToken", token);
}
Comment thread
Goder-0 marked this conversation as resolved.
}
}

} catch (BusinessException e) {
throw new MessagingException(e.getMessage());
try {
if (token == null) {
throw new BusinessException(JwtErrorCode.EMPTY_TOKEN);
}

} catch (Exception e) {
throw new MessagingException("서버 내부 오류로 연결에 실패했습니다.");
if (jwtTokenProvider.validateAccessToken(token)) {

if (StompCommand.CONNECT.equals(command)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
accessor.setUser(authentication);
}
}

} catch (BusinessException e) {
log.warn("웹소켓 인증/만료 에러 차단: {}", e.getMessage());
throw new MessagingException(e.getMessage());

} catch (Exception e) {
log.error("웹소켓 서버 내부 오류", e);
throw new MessagingException("서버 내부 오류로 연결에 실패했습니다.");
}
}
}

Expand Down
Loading
Loading