目前 打算给我的ECS服务器做一个状态监控
前端准备使用Echarts做图表 服务器信息通过云服务器 ECS API 获取,然后再自己加一些信息 然后通过websocket 发送状态数据
Echarts 已经比较熟悉了,有再项目中使用过 Websocket 也有在项目中使用过,不过是在
进入正题
Springboot 配置 Websocket
网上大量文章都是在使用过时的配置方式
springboot 已经帮助我们做了很多配置 推荐优先查看官方文档
我的Springboot版本为2.1.4.RELEASE
第一步 添加依赖
pom.xml
//others dependency
org.springframework.boot
spring-boot-starter-websocket
第二步 创建接收前端消息类
WebsocketMessage.groovy ps:一定需要set/get方法
class WebsocketMessage {
/** 我使用的是Groovy* 也可以定义 String data* 消息内容*/
def data
/** 发送的目的地 */
String destination
def getData() {
return data
}
void setData(data) {
this.data = data
}
String getDestination() {
return destination
}
void setDestination(String destination) {
this.destination = destination
}
}
第三步 创建Websocket控制器
MonitorController.groovy
@Controller
class MonitorController {
@MessageMapping("/hello") //响应前端发送"/app/hello"地址 @SendTo("/topic/helloEcho") // 告诉springboot将方法返回值通过websocket发送到 "/topic/helloEcho"地址 public ResponseData helloEcho(WebsocketMessage message) throws Exception {
/** 我这里是返回了自定义的响应对象,并放入了用户发送来的消息 */
return new ResponseData(data: "echo:${message.getData()}")
}
}
第四步 创建Websocket 配置类
WebSocketConfig.java特别注意
registry.addEndpoint("/hoody-websocket").setAllowedOrigins("*").withSockJS();
开发环境我使用了通配符, 正式环境应该传入 允许的源 可以通过profile控制
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/** 允许使用topic,并且所有主题地址前缀为"/app" */
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
/*** 添加一个/hoody-websocket端点,客户端就可以通过这个端点来进行连接;* withSockJS作用是添加SockJS支持,* setAllowedOrigins(String... var1) 指定可以跨域访问的地址*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hoody-websocket").setAllowedOrigins("*").withSockJS();
}
}
第五步 前端Vue 使用 Stomp 和 SockJS 访问
import Stomp from 'stompjs'
import SockJS from 'sockjs-client'
export default {
name: 'websocket',
data() {
return {
stompClient: null,
timer: null,
}
},
mounted() {
this.initWebSocket()
},
beforeDestroy: function() {
/** 页面离开时断开连接,清除定时器 */
this.disconnect()
clearInterval(this.timer)
},
methods: {
/** 绑定 按钮发送 */
clickBtn() {
this.sendWsMessage("Hi,world")
},
sendWsMessage(msg) {
/** 向'/app/hello' 发送 消息,需要将对象转换为JSON 字符串this.stompClient.send('/app/hello', {},JSON.stringify({ data: msg }),)},/** 收到消息处理 */
onMessage(msg) {
this.$message.success('收到消息:' + msg) // msg.body存放的是服务端发送给我们的信息 },
/** 初始化Socket连接 */
initWebSocket() {
this.connection()
const that = this
// 断开重连机制,尝试发送消息,捕获异常发生时重连 10秒一次 this.timer = setInterval(() => {
try {
that.stompClient.send('test')
} catch (err) {
that.$message.warning('断线了: ' + err)
that.connection()
}
}, 10000)
},
connection() {
// 建立连接对象 const socket = new SockJS('/api/hoody-websocket')
// 获取STOMP子协议的客户端对象 this.stompClient = Stomp.over(socket)
// 定义客户端的认证信息,按需求配置 // 向服务器发起websocket连接 const that = this
this.stompClient.connect({ 'X-Token': that.$store.getters.token }, () => {
this.stompClient.subscribe('/topic/helloEcho', (msg) => { // 订阅服务端提供的某个topic that.onMessage(msg.body)
msg.ack()
})
// 用户加入接口 }, (err) => {
// 连接发生错误时的处理函数 that.$message.error(err)
})
}, // 连接 后台 disconnect() {
if (this.stompClient) {
this.stompClient.disconnect()
}
} // 断开连接 }
}
现在可以愉快的访问了
第七步 了解shiro-spring-boot-web-starter 帮我们做了的事
在浏览器开发者工具 的Console 可以看到连接成功的信息
>>> CONNECT
X-Token:4dead694-eb84-496f-ba93-802b6fcbe939
accept-version:1.1,1.0
heart-beat:10000,10000
------------------------------------------------------
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:User{id=34, username='111',password='dab73e9a6c79ec96117d1474f68f2249'}
------------------------------------------------------
connected to server
------------------------------------------------------
>>> SUBSCRIBE
id:sub-0
destination:/topic/helloEcho
连接成功信息,居然从服务器返回了当前登录用户的账号信息,但是为什么会把密码也搞过来啊 这个信息看起来很像我的User.toString() ~~还好我没把盐值也发出来~~ 于是打了断点看了一下流程
1. 服务器收到 ws://localhost/hoody-websocket/ 连接后将会执行getName()方法 org.springframework.web.servlet.FrameworkServlet
protected String getUsernameForRequest(HttpServletRequest request) {
Principal userPrincipal = request.getUserPrincipal();
return (userPrincipal != null ? userPrincipal.getName() : null);
}
2.如果你的用户类没有getName()方法 他就会!!! 获取 整个对象的toString() ShiroHttpServletRequest
public String getName() {
return getObject().toString();
}
所以,检查你的User类,不要把密码也搞出去了,就算是加密的也不要
原文发布与我的个人博客Springboot 接入Websocket (with Shiro)hoody.tech