导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
配置
package com.qi.demo1.config; import com.qi.demo1.interceptor.MyChannelInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** * @author qyb * @version 1.0 * @date 2022/7/26-11:34 */ @Configuration @EnableWebSocketMessageBroker public class WebsocketConfig implements WebSocketMessageBrokerConfigurer { @Autowired MyChannelInterceptor myChannelInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { 使用sockjs // registry.addEndpoint("/socket") // .setAllowedOrigins("*") // .withSockJS(); // 使用原生 registry.addEndpoint("/socket") .setAllowedOrigins("*"); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(myChannelInterceptor); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 点对点 默认为/user registry.setUserDestinationPrefix("/user"); 接收客户消息前缀 // registry.setApplicationDestinationPrefixes("/app"); // // 声明在/user、/topic可以像客户端一样发送消息 (声明必须加/user ,否则,一对一消息无法发送) registry.enableSimpleBroker("/user","/topic"); } }
配置客户端入口通道拦截器
用于认证授权,未登录无法连接
package com.qi.demo1.interceptor; import com.qi.demo1.entity.AuthUser; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Component; import java.security.Principal; import java.util.List; /** * @author qyb * @version 1.0 * @date 2022/7/27-10:28 */ @Slf4j @Component public class MyChannelInterceptor implements ChannelInterceptor { @Override public Message<?> preSend(@NotNull Message<?> message, @NotNull MessageChannel channel) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); StompCommand command = accessor.getCommand(); if (StompCommand.CONNECT.equals(command)) { // 认证校验,没有登录的用户不允许连接 List<String> authorization = accessor.getNativeHeader("Authorization"); if (authorization != null && authorization.size() > 0) { String token = authorization.get(0); if (!"123456".equals(token)) { throw new RuntimeException("认证失败"); } accessor.setUser(new AuthUser(token)); log.info("成功连接用户{}", token); } } else if (StompCommand.DISCONNECT.equals(command)) { Principal user = accessor.getUser(); log.info("断开用户{}", user); } System.out.println(message); return message; } }
前端代码
import {Client} from '@stomp/stompjs' export default class WebSocketApi { constructor() { this.client = new Client({ brokerURL: 'ws://localhost:8090/socket', ///不能赋值,因为以后用SockJS来代替 connectHeaders: { "Authorization": "123456" }, debug:(msg)=> console.log(msg), reconnectDelay: 10000, //重连时间 heartbeatIncoming: 4000, heartbeatOutgoing: 4000, //错误 onStompError: () => console.log(连接失败)//这里不需要重连了,新版自带重连
})
}
connect(url, callback) {
this.client.onConnect = () => this.client.subscribe(url, callback)
this.client.activate()
}
disConnect() {
if (this.client != null) this.client.deactivate()
}
}
前端使用
created() {
this.websocket=new WebsocketApi()
this.websocket.connect("/user/123456/message/chat",(res)=>{
console.log(res.body);
console.log(111);
})
},
后端发送消息的接口
package com.qi.demo1.controller;
import com.qi.demo1.common.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.user.SimpUser;
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Set;
/**
* @author qyb
* @version 1.0
* @date 2022/7/27-11:42
*/
@RestController
public class WsController {
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
@Autowired
SimpUserRegistry simpUserRegistry;
@GetMapping("/send")
public R<?> sendToUser(String msg) {
Set<SimpUser> users = simpUserRegistry.getUsers();
System.out.println(users);
simpMessagingTemplate.convertAndSendToUser("123456","/message/chat","123467");
return R.ok();
}
}
参考连接 : springboot中通过stomp方式来处理websocket及token权限鉴权相关 ;
将 STOMP 与 SockJS 一起使用
Spring Websocket+Stomp 防踩坑实战
Spring使用WebSocket、SockJS、STOMP实现消息功能