1.简单的实现了聊天室功能,注意页面刷新后聊天记录不会保存,后端没有做消息的持久化
2.后端用户的识别只简单使用Session用户的身份
0.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
1.配置WebSocketConfigurer配置类
package com.example.config;
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.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 javax.servlet.http.HttpSession;
import java.util.Map;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfigurer implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS()
.setInterceptors(new HttpHandshakeInterceptor());
}
public class HttpHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
//使用session中的user
String user = (String) session.getAttribute("user");
if(user!=null){
attributes.put("user", user);
}
}
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) { }
}
}
2.MessageHandler用于处理接收消息
package com.example.webController;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import javax.servlet.http.HttpSession;
@Controller
public class MessageHandler {
@MessageMapping("/chat")
@SendTo("/topic/messages")
public String chat(@PathVariable("messages") String message, SimpMessageHeaderAccessor headerAccessor) {
// 获取用户数据
Object user = headerAccessor.getSessionAttributes().get("user");
System.out.println("User: " + user);
return user.toString()+":" + message;
}
//登录
@GetMapping("login/{userName}")
public void login(@PathVariable String userName, HttpSession session) {
System.out.println("User " + userName);
session.setAttribute("user", userName);
}
}
3.前端页面
先登录后发送消息
4.前端代码
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Demo</title>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs/lib/stomp.min.js"></script>
<style>
#chatBox {
width: 60%;
margin: auto;
height: 500px;
border: 1px solid black;
padding: 10px;
border-radius: 7px;
}
.chat-message {
height: 85%;
overflow: auto;
}
.flex-row, .flex-row-reverse {
display: flex;
justify-content: space-between;
padding: 10px 0;
}
.message {
max-width: 60%;
padding: 10px;
border-radius: 10px;
}
.user {
background-color: #7986cb;
color: white;
}
.assistant {
background-color: #ffe082;
}
#inputMessage {
width: 80%;
padding: 10px;
border-radius: 10px;
border: 1px solid black;
margin-top: 10px;
}
#sendButton {
width: 15%;
margin-left: 5%;
padding: 11px;
background-color: #ff7043;
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="chat">
<div id="chatBox">
<div class="chat-message">
<div class="flex-row">
<div class="message user">
<p>Hello</p>
</div>
</div>
<div class="flex-row-reverse">
<div class="message assistant">
<p>Hi, How can I help you?</p>
</div>
</div>
</div>
<input type="text" id="inputMessage" placeholder="Type message.." />
<button id="sendButton" onclick="send()">Send</button>
</div>
<input type="text" id="login" placeholder="输入用户名。。。" />
<button onclick="login()">Login</button>
</div>
<script>
var socket = new SockJS('/ws');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/messages', function(message) {
var div = document.createElement('div');
div.className = "flex-row-reverse";
var messageDiv = document.createElement('div');
messageDiv.className = "message assistant";
var p = document.createElement('p');
p.textContent = message.body;
messageDiv.appendChild(p);
div.appendChild(messageDiv);
document.querySelector('.chat-message').appendChild(div);
});
});
function login() {
var value = document.getElementById('login').value;
//发送get请求
fetch('/login/' + value)
//刷新页面
window.location.reload();
}
function send() {
var inputValue = document.getElementById('inputMessage').value;
var div = document.createElement('div');
div.className = "flex-row";
// var messageDiv = document.createElement('div');
// messageDiv.className = "message user";
// var p = document.createElement('p');
// p.textContent = inputValue;
// messageDiv.appendChild(p);
// div.appendChild(messageDiv);
document.querySelector('.chat-message').appendChild(div);
stompClient.send("/app/chat", {}, inputValue);
document.getElementById('inputMessage').value = '';
}
</script>
</body>
</html>