上一篇博客我们介绍了在Spring Boot框架下使用WebSocket实现消息推送,消息推送是一对多,服务器发消息发送给所有的浏览器,这次我们来看看如何使用WebSocket实现消息的一对一发送,模拟的场景就是利用网页来实现两个人在线聊天。OK,那我们来看看这个要怎么实现。

引入Spring Security并配置

由于这里涉及到多个用户之间互相传递消息的问题,涉及到的权限管理问题我使用Spring Security来处理,关于Spring Security的更多详细资料小伙伴们可以参考下面几个资料:

1.SpringMVC4零配置–SpringSecurity相关配置【SpringSecurityConfig】

2.spring security的原理及教程

3.spring security教程

OK ,关于Spring Security的更多话题这里不再多说,这里仅仅来说一下在我们自己的项目中如何使用Spring Security。

引入Spring Security

引入方式很简单,直接在Maven中添加如下依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>1.4.2.RELEASE</version>
        </dependency>

配置Spring Security

配置方式也简单,新建类WebSecurityConfig,代码如下:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //设置拦截规则
                .antMatchers("/")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                //开启默认登录页面
                .formLogin()
                //默认登录页面
                .loginPage("/login")
                //默认登录成功跳转页面
                .defaultSuccessUrl("/chat")
                .permitAll()
                .and()
                //设置注销
                .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("lenve").password("111").roles("USER")
                .and()
                .withUser("sang").password("222").roles("USER");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //设置不拦截规则
        web.ignoring().antMatchers("/resources/static/**");
    }
}

关于这里的代码我做如下几点说明,

1.在configure(HttpSecurity http)方法中,我们首先设置拦截规则,设置默认登录页面以及登录成功后的跳转页面

2.在configure(AuthenticationManagerBuilder auth)方法中,我们定义两个用户,设置用户名、用户密码、用户角色等信息。

3.在configure(WebSecurity web)方法中设置静态资源不被拦截。

配置WebSocket

WebSocket的配置和我们上篇博客中WebSocket的配置方式一致,如下:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/endpointChat").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue");
    }
}

和上篇文章的WebSocket配置比起来,这里有两个变化:

1.endpoint的名字变了,当然这个无所谓,取啥名字都行,关键是要和html文件中的对应上。

2.消息代理换了

其他都一模一样,小伙伴对这里有疑问可以查看上篇博客

配置控制器

由于我这里只有两个用户,所以我在控制器里的处理尽可能简单一些,假如这条消息是由A用户发来的,那么毫无疑问这条消息要转发给B用户,如果这条消息是由B用户发来的,那么毫无疑问这条消息要转发给A,小伙伴们在使用的过程中可以根据具体的业务逻辑来灵活配置,我这里就直接硬编码,OK,我们来看看控制器的代码:

@Controller
public class WsController {
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @MessageMapping("/chat")
    public void handleChat(Principal principal, String msg) {
        if (principal.getName().equals("sang")) {
            messagingTemplate.convertAndSendToUser("lenve", "/queue/notifications", principal.getName() + "给您发来了消息:" + msg);
        }else{
            messagingTemplate.convertAndSendToUser("sang", "/queue/notifications", principal.getName() + "给您发来了消息:" + msg);
        }
    }
}

关于这一段代码,我说如下几点:

1.SimpMessagingTemplate这个类主要是实现向浏览器发送消息的功能。

2.在Spring MVC中,可以直接在参数中获取Principal,Principal中包含有当前用户的用户名。

3.convertAndSendToUser方法是向用户发送一条消息,第一个参数是目标用户用户名,第二个参数是浏览器中订阅消息的地址,第三个参数是消息本身。

登录页面

在src/main/resources/templates目录下新建login.html文件,内容如下:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="UTF-8" />
    <title>登录</title>
</head>
<body>
<div th:if="${param.error}">
    无效的账号或密码
</div>
<div th:if="${param.logout}">
    你已注销
</div>
<form th:action="@{/login}" method="post">
    <div><label>账号:<input type="text" name="username" /></label></div>
    <div><label>密码:<input type="password" name="password" /></label></div>
    <div><input type="submit" value="登录" /></div>
</form>
</body>
</html>

这个一个非常普通的html页面,我就不再赘述了,注意form表单的提交位置。

聊天页面

在src/main/resources/templates文件夹下新建文件chat.html文件,内容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>聊天室</title>
    <script th:src="@{js/sockjs.min.js}"></script>
    <script th:src="@{js/stomp.js}"></script>
    <script th:src="@{js/jquery-3.1.1.js}"></script>
</head>
<body>
<p>聊天室</p>
<form id="sangForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit" value="发送"/>
</form>
<script th:inline="javascript">
    $("#sangForm").submit(function (e) {
        e.preventDefault();
        var textArea = $("#sangForm").find('textarea[name="text"]');
        var text = textArea.val();
        sendSpittle(text);
        textArea.val('');
    });
    var sock = new SockJS("/endpointChat");
    var stomp = Stomp.over(sock);
    stomp.connect('guest','guest',function (frame) {
        stomp.subscribe("/user/queue/notifications", handleNotification);
    });
    function handleNotification(message) {
        $("#output").append("<b>Received: "+message.body+"</b><br/>")
    }
    function sendSpittle(text) {
        stomp.send("/chat", {}, text);
    }
    $("#stop").click(function () {
        sock.close();
    });
</script>
<div id="output"></div>
</body>
</html>

这个页面也没有太多的难点,大部分知识点都和上文说的一样,有不懂的小伙伴可以查看上文,还是有几个小的知识点我再来说一下:

1.stomp中的connect方法用来连接服务端,连接成功之后注册监听,在注册监听的时候,注册的地址/user/queue/notifications比WebSocket配置文件中的多了一个/user,这个/user是必不可少的,使用了它消息才会点对点传送。

2.收到消息后在handleNotification方法中处理,实际上就是把收到的内容添加到id为output的div中

OK,这里其他的知识点都比较简单,我就不再赘述了。

配置页面映射

这个我们也写过多次了,有疑问的小伙伴查看这篇博客SpringMVC常用配置,如下:

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("/login");
        registry.addViewController("/chat").setViewName("/chat");
    }
}

然后运行项目,打开两个不同的浏览器(同一个浏览器中session是共享的),分别用两个用户进行登录,登录成功之后就可以嗨起来啦!运行效果如下图:

以上。

本案例下载地址:

本案例GitHub地址

更多关于Spring、SpringMVC、Spring Boot的资料请移步这里https://github.com/lenve/JavaEETest

参考资料:

《JavaEE开发的颠覆者 Spring Boot实战》第七章

在Spring Boot框架下使用WebSocket实现聊天功能的更多相关文章

  1. 在Spring Boot框架下使用WebSocket实现消息推送

    Spring Boot的学习持续进行中.前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目(使用Spring Boot开发Web项目)以及怎样为我们的Project添加HTTPS的 ...

  2. Spring Boot 框架下使用MyBatis访问数据库之基于XML配置的方式

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML ...

  3. 手把手写一个基于Spring Boot框架下的参数校验组件(JSR-303)

    前言 之前参与的新开放平台研发的过程中,由于不同的接口需要对不同的入参进行校验,这就涉及到通用参数的校验封装,如果不进行封装,那么写出来的校验代码将会风格不统一.校验工具类不一致.维护风险高等其它因素 ...

  4. 初识Spring Boot框架(二)之DIY一个Spring Boot的自动配置

    在上篇博客初识Spring Boot框架中我们初步见识了SpringBoot的方便之处,很多小伙伴可能也会好奇这个Spring Boot是怎么实现自动配置的,那么今天我就带小伙伴我们自己来实现一个简单 ...

  5. 初识Spring Boot框架

    前面的铺垫文章已经连着写了六篇了,主要是介绍了Spring和SpringMVC框架,小伙伴们在学习的过程中大概也发现了这两个框架需要我们手动配置的地方非常多,不过做JavaEE开发的小伙伴们肯定也听说 ...

  6. spring boot 框架设计步骤

    spring boot 框架设计步骤: 1.poem.xml配置 2.application.yml配置 3.entiry实体 4.realm.Myrealm extends AuthorizingR ...

  7. spring boot 学习(二)spring boot 框架整合 thymeleaf

    spring boot 框架整合 thymeleaf spring boot 的官方文档中建议开发者使用模板引擎,避免使用 JSP.因为若一定要使用 JSP 将无法使用. 注意:本文主要参考学习了大神 ...

  8. (Spring Boot框架)快速入门

    Spring Boot 系列文章推荐 Spring Boot 入门 Spring Boot 属性配置和使用 Spring Boot 集成MyBatis Spring Boot 静态资源处理 今天介绍一 ...

  9. [原创]Spring boot 框架构建jsp web应用

    说明 Spring boot支持将web项目打包成一个可执行的jar包,内嵌tomcat服务器,独立部署 为支持jsp,则必须将项目打包为war包 pom.xml中设置打包方式 <packagi ...

随机推荐

  1. html标记语言 --超链接

    html标记语言 --超链接 四.超链接 1. 基本语法 <a href="" target="打开方式" name="页面锚点名称" ...

  2. CSS揭秘(四)视觉效果

    Chapter 4 1. 单侧投影 为元素设置投影可以使用 box-shadow 属性,指定三个长度值(X轴偏移量.Y轴偏移.模糊半径)与一个颜色值 要想只在底部设置投影,需要用到第四个参数:投影的扩 ...

  3. 学习React系列(三)——Refs和Dom

    一.适用于以下场景: 1.控制焦点,文本选择,或者媒体控制 2.触发必要的动画 3.整合第三方dom库 二.不要过度使用ref 如果想通过ref来改变state,那么换一种方式-变量提升可能会更好. ...

  4. iOS 检测屏幕是否锁定 🔓 / 🔒

    1. 导入头文件 #import <notify.h> 2. 给 CFNotificationCenter 添加观察者 - (void)addLockStatusObserver { CF ...

  5. YARN中FIFO、Capacity以及Fari调度器的详细介绍

    (1)FIFO Scheduler 将所有的Applications放到队列中,先按照作业的优先级高低.再按照到达时间的先后,为每个app分配资源.如果第一个app需要的资源被满足了,如果还剩下了资源 ...

  6. C语言中关于运算符优先级别

    在一一个表达式中可能有多个不同的运算符结合起来,由于运算符的优先级别不一样,可能会形成得到的结果不同. 优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级. 对于相同的优先级, ...

  7. ABP领域层知识回顾之---实体

    标题:重温ABP领域层 1. 前言  最近一段时间一直在看<ABP的开发指南>(基于DDD的经典分层架构思想).因为之前一段时间刚看完<领域驱动设计:软件核心复杂性应对之道>, ...

  8. 计蒜客NOIP模拟赛(2) D2T1 劫富济贫

    [问题描述] 吕弗·普自小从英国长大,受到骑士精神的影响,吕弗·普的梦想便是成为一位劫富济贫的骑士. 吕弗·普拿到了一份全国富豪的名单(不在名单上的都是穷人),上面写着所有富豪的名字以及他们的总资产, ...

  9. 2017ACM/ICPC广西邀请赛-重现赛 1010.Query on A Tree

    Problem Description Monkey A lives on a tree, he always plays on this tree. One day, monkey A learne ...

  10. hdu3966 点权模板-树链部分

    Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...