基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【九】【整合websocket】

细心的朋友一定在上一章节的程序运行中发现了这样的一个报错:


      这个报错的造成是因为在大家访问首页的时候,我前端开启了websocket的连接,但是我们的后端并没有对websocket进行相应的支持,因此本章将教大家如何开启对websocket的支持。

     对于websocket的maven依赖已经在本工程中引入了因此此处就不在叙述了,在我们的sys的config包底下新建websocket文件夹如下所示:


     接着我们增加websocket的配置文件分别是:InMessage.java(消息接收实体)、OutMessage.java(消息发送实体)、SocketSessionRegistry.java(用户session记录)、STOMPConnectEventListener.java(监听类)、WebSocketConfig.java(配置类)代码如下:

    

/**
 * 消息接收实体
 */
public class InMessage {

    private String name;
    private String id;

    public InMessage() {
    }

    public InMessage(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 消息推送实体
 */
public class OutMessage {

    private String content;

    public OutMessage() {
    }

    public OutMessage(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

}

/**
 * Created by baiguantao on 2017/8/4.
 * 用户session记录类
 */
public class SocketSessionRegistry{
    //this map save every session
    //这个集合存储session
    private final ConcurrentMap<String, Set<String>> userSessionIds = new ConcurrentHashMap();
    private final Object lock = new Object();

    public SocketSessionRegistry() {
    }

    /**
     *
     * 获取sessionId
     * @param user
     * @return
     */
    public Set<String> getSessionIds(String user) {
        Set set = (Set)this.userSessionIds.get(user);
        return set != null?set: Collections.emptySet();
    }

    /**
     * 获取所有session
     * @return
     */
    public ConcurrentMap<String, Set<String>> getAllSessionIds() {
        return this.userSessionIds;
    }

    /**
     * register session
     * @param user
     * @param sessionId
     */
    public void registerSessionId(String user, String sessionId) {
        Assert.notNull(user, "User must not be null");
        Assert.notNull(sessionId, "Session ID must not be null");
        Object var3 = this.lock;
        synchronized(this.lock) {
            Object set = (Set)this.userSessionIds.get(user);
            if(set == null) {
                set = new CopyOnWriteArraySet();
                this.userSessionIds.put(user, (Set<String>) set);
            }
            // 当最迟登陆的时间和当前时间的年月日不匹配的时候清空session缓存
            User userLogin = UserInfo.getUser();
            if(userLogin!=null){
                if(!DateUtil.format(userLogin.getLastLoginDate(), DatePattern.NORM_DATE_FORMAT).equalsIgnoreCase(DateUtil.format(new Date(), DatePattern.NORM_DATE_FORMAT))){
                    set = new CopyOnWriteArraySet();
                    this.userSessionIds.put(user, (Set<String>) set);
                }
            }
            ((Set)set).add(sessionId);
        }
    }

    public void unregisterSessionId(String userName, String sessionId) {
        Assert.notNull(userName, "User Name must not be null");
        Assert.notNull(sessionId, "Session ID must not be null");
        Object var3 = this.lock;
        synchronized(this.lock) {
            Set set = (Set)this.userSessionIds.get(userName);
            if(set != null && set.remove(sessionId) && set.isEmpty()) {
                this.userSessionIds.remove(userName);
            }

        }
    }
}

/**
 * Created by baiguantao on 2017/8/4.
 * STOMP监听类
 * 用于session注册 以及key值获取
 */
public class STOMPConnectEventListener  implements ApplicationListener<SessionConnectEvent> {

    @Autowired
    SocketSessionRegistry webAgentSessionRegistry;

    @Override
    public void onApplicationEvent(SessionConnectEvent event) {
        StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
        //login get from browser
        String agentId = sha.getNativeHeader("login").get(0);
        String sessionId = sha.getSessionId();
        System.out.println("接收的sessionId是:"+sessionId);
        webAgentSessionRegistry.registerSessionId(agentId,sessionId);
    }

}


/*
* 类描述:
* @auther linzf
* @create 2017/8/8 0008 
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");// /users 默认通知
        config.setApplicationDestinationPrefixes("/app");
        //设置前缀  默认是user 可以修改  点对点时使用
        config.setUserDestinationPrefix("/ricky/");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ricky-websocket").withSockJS();
    }


    @Bean
    public SocketSessionRegistry SocketSessionRegistry(){
        return new SocketSessionRegistry();
    }
    @Bean
    public STOMPConnectEventListener STOMPConnectEventListener(){
        return new STOMPConnectEventListener();
    }

}

接着我们重新加载我们的代码,并运行我们的项目,当我们登陆成功以后我们会看到以下的截图说明我们的websocket已经配置成功了:


       那么我们该如何运用我们的websocket呢,假设我们这里模拟这样的一个场景:用户在手机端下了一个订单,那么后台登陆的当前商户需要收到这个订单的信息那么我们来模拟实现这么一个场景。

       首先我们在sys的controller包底下创建一个类WebsocketController.java内容如下:

/*
* 类描述:
* @auther linzf
* @create 2017/12/11 0011 
*/
@Controller
@RequestMapping("/websocket")
public class WebsocketController {

    /**session操作类*/
    @Autowired
    private SocketSessionRegistry webAgentSessionRegistry;
    /**消息发送工具*/
    @Autowired
    private SimpMessagingTemplate template;

    /**
     * 功能描述:给全局推送消息
     */
    @RequestMapping(value = "/sendAll",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public void sendAll(){
        Map<String,Set<String>> all = webAgentSessionRegistry.getAllSessionIds();
        JSONObject jobj = new JSONObject();
        jobj.put("test","test");
        all.forEach((k,v)->{
            v.forEach(x->{
                template.convertAndSendToUser(x,"/topic/greetings",new OutMessage(jobj.toString()),createHeaders(x));
            });
        });
    }

    /**
     * 功能描述:给指定账号推送消息
     */
    @RequestMapping(value = "/sendUser",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public void senduUser(){
        // 此处账号已经被写死为hyll 大家可以根据自己的实际业务来修改此处的代码
        Set<String> keys = webAgentSessionRegistry.getSessionIds("hyll");
        JSONObject jobj = new JSONObject();
        jobj.put("hyll","hyll");
        keys.forEach(x->{
            template.convertAndSendToUser(x,"/topic/greetings",new OutMessage(jobj.toString()),createHeaders(x));
        });
    }


    /**
     * 功能描述:组装JSON数据的头部数据
     * @param sessionId
     * @return
     */
    private MessageHeaders createHeaders(String sessionId) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);
        return headerAccessor.getMessageHeaders();
    }

}

我们重新加载我们的代码并运行我们的程序然后登陆我们的首页,接着分别调用以上的两个接口(http://127.0.0.1:8080/websocket/sendAll 、http://127.0.0.1:8080/websocket/sendUser):我们可以看到结果如下:


这两个就是从后端推送到页面的信息,该块的js逻辑大家可以在main.html页面找到,大家根据自己的实际业务来扩展该块的内容如下:


到此处我们已经整合好了我们的websocket的代码,整个代码的GitHub地址是:https://github.com/185594-5-27/csdndemo/tree/master-websocket


上一篇文章地址:基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【八】【完善整个项目】


下一篇文章地址:正在编写中


QQ交流群:578746866




©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页