728x90
반응형
SMALL
- WebSocket 설정
WebSocket을 활성화 하기 위해 @EnableWebSocket 어노테이션을 사용합니다.
javaCopy code
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatHandler(), "/chat/{roomId}")
.setAllowedOrigins("*")
.withSockJS();
}
@Bean
public WebSocketHandler chatHandler() {
return new ChatHandler();
}
}
2. WebSocket Handler 구현
WebSocket 메시지를 처리하는 핸들러를 구현합니다. 아래는 간단한 예시입니다.
javaCopy code
public class ChatHandler extends TextWebSocketHandler {
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String roomId = getRoomId(session);
sessions.put(roomId, session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String roomId = getRoomId(session);
TextMessage msg = new TextMessage("[" + roomId + "] " + message.getPayload());
broadcast(roomId, msg);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String roomId = getRoomId(session);
sessions.remove(roomId);
}
private String getRoomId(WebSocketSession session) {
return session.getUri().getPath().split("/")[2];
}
private void broadcast(String roomId, TextMessage message) throws IOException {
for (WebSocketSession session : sessions.values()) {
if (session.isOpen() && roomId.equals(getRoomId(session))) {
session.sendMessage(message);
}
}
}
}
3. 인증 및 권한 부여
스프링 시큐리티를 사용하여 인증과 권한 부여를 구현할 수 있습니다. 아래는 간단한 예시입니다.
javaCopy code
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4. 토큰 기반 인증
로그인 후에 클라이언트는 서버로부터 JWT 토큰을 받아서 이를 이용하여 채팅방에 접근할 수 있습니다. 아래는 간단한 예시입니다.
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtTokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
로그인 성공시 JWT 토큰을 반환하는 응답 클래스를 정의합니다.
javaCopy code
public class JwtAuthenticationResponse {
private String accessToken;
public JwtAuthenticationResponse(String accessToken) {
this.accessToken = accessToken;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}
JWT 토큰을 생성하는 유틸리티 클래스를 구현합니다.
javaCopy code
@Component
public class JwtTokenProvider {
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(userPrincipal.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
}
5. 채팅방 구현
WebSocket 세션 ID를 이용하여 각 채팅방을 구분합니다. 채팅방은 각 사용자별로 생성되며, 클라이언트는 채팅방의 ID를 이용하여 특정 채팅방에 접속합니다.
javaCopy code
@RestController
@RequestMapping("/api/chat")
public class ChatController {
@MessageMapping("/{roomId}")
@SendTo("/topic/{roomId}")
public ChatMessage sendMessage(@DestinationVariable String roomId, ChatMessage message) {
return message;
}
}
6. 데이터베이스 저장
채팅방에서 주고받은 메시지를 데이터베이스에 저장할 수 있습니다. 메시지 모델과 메시지 레파지
토리를 구현합니다.
javaCopy code
@Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
List<Message> findAllByRoomIdOrderByCreatedAtAsc(Long roomId);
}
7. UI 구현
웹 브라우저에서 채팅방을 사용할 수 있는 UI를 구현합니다. 아래는 간단한 예시입니다.
htmlCopy code
<!DOCTYPE html>
<html>
<head>
<title>Chat Room</title>
<script src="/webjars/sockjs-client/1.0.2/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/2.3.3/stomp.min.js"></script>
<script>
var stompClient = null;
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/' + roomId, function (message) {
showMessage(JSON.parse(message.body));
});
});
}
function sendMessage() {
var message = document.getElementById("message").value;
stompClient.send("/app/chat/" + roomId, {}, JSON.stringify({content: message}));
document.getElementById("message").value = "";
}
function showMessage(message) {
var messageArea = document.getElementById("messageArea");
messageArea.innerHTML += message.content + "\\n";
}
window.onload = function () {
connect();
};
</script>
</head>
<body>
<div>
<h1>Chat Room</h1>
<p>Room ID: <span id="roomId"></span></p>
<textarea id="messageArea" rows="10" cols="50"></textarea>
<br>
<input type="text" id="message" name="message">
<button onclick="sendMessage()">Send</button>
</div>
<script>
var urlParams = new URLSearchParams(window.location.search);
var roomId = urlParams.get('roomId');
document.getElementById("roomId").innerHTML = roomId;
</script>
</body>
</html>
728x90
반응형
LIST
'개발 > Spring' 카테고리의 다른 글
웹소캣 - 채팅 기본 구현 (테스트 코드 연습) (0) | 2023.04.09 |
---|---|
웹소켓 알림 메세지 - session (0) | 2023.04.09 |
yml 파일이란 ? (0) | 2023.03.12 |
WebSock 구현 (0) | 2023.03.07 |
JWT 장점과 단점 (0) | 2023.02.15 |