Spring Boot之WebSocket实现点对点通讯

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chenbetter1996/article/details/86728713

1. 后台注册一个WebSocket

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        // 添加一个名为 "/endpointChat" 的基于STOMP子协议的节点(即服务器端socket)
        stompEndpointRegistry.addEndpoint("/endpointChat").withSockJS();
    }
}

使用的是WebSocket的子协议STOMP

2. 自定义数据库认证方式

@Service
public class CustomUserService implements UserDetailsService {
    private static final Log log = LogFactory.getLog(CustomUserService.class);

    @Autowired
    private UserMapper userMapper;

    /**
     * 重写 loadUserByUsername 方法获取 userDetails 类型用户
     * SysUser 已经实现 UserDetails
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = userMapper.findByUsername(username);
        if (user != null) {
            log.info("username存在");
            log.info("打印: " + user);

            // 用户权限
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            //用于添加用户的权限。只要把用户权限添加到authorities[本项目固定是用户角色]
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

            return new org.springframework.security.core.userdetails.User(user.getUsername(),
                    user.getPassword(), authorities);
        } else {
            throw new UsernameNotFoundException("admin: " + username + " do not exist!");
        }
    }
}
  1. 认证方式有很多,可以直接使用内存存储用户,这里是使用通用的数据库存储用户信息。
  2. 直接实现 org.springframework.security.core.userdetails.UserDetailsService接口的loadUserByUsername方法即可。这个方法返回的是一个org.springframework.security.core.userdetails.UserDetails对象
  3. 作用是根据参数用户名,查找用户信息,然后交由Spring Security判断密码是否正确(Spring Security会对前端的密码做BCrypt加密然后和得到的用户信息的密码作比较,BCrypt是多对多)
  4. 重要的还有用户的角色,根据不同角色可以编写不同的功能,这也是Spring Security的一个类的必须属性。企业用户角色,这里暂时都采用普通用户类型,还有ROLE_ADMIN等等。

3. 再WebSecurityConfig里面配置加入上面的数据库认证方式

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    UserDetailsService myCustomUserService() {
        return new CustomUserService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myCustomUserService());
    }
    ....
}

认证方式对应的是这个 protected void configure(AuthenticationManagerBuilder auth) throws Exception {…} 方法。
里面可以配置各种认证方式,比如提到过的内存验证。自定义数据库验证的就得实现那个接口。如上这种方式配置。

4. 客户端订阅

var sock = new SockJS("/chatroom/endpointChat");
var stomp = Stomp.over(sock);
stomp.connect('guest', 'guest', function (frame) {
    stomp.subscribe("/user/notification", showGetMsg);
});
  1. /endpointChat就是后台创建的WebSocket,前面的/chatroom只是个人项目的应用上下文。这里可以使用 "./endpointChat"替换。
  2. 这里已客户身份登录,stomp.subscribe("/user/notification", showGetMsg);是订阅服务器端的通知。user就是通用的普通用户角色,服务器可以根据订阅了/notification的相应客户端自由转发消息。 showGetMeg是绑定的自定义回调函数,当服务器转发消息来的时候,会自动调用这个函数。参数是message。如下
/**
 * 显示收到的信息
 * @param message
 */
function showGetMsg(message) {
    console.log("收到服务器转发的的信息(别人发来的):" + message);
    // 这个API时间格式化函数别人写的是,这里用hh ...
    var now = new Date().Format("yyyy-MM-dd hh:mm:ss");
    // 这里显示还没有完善(此外:异步回调的message是一个http数据包,数据在body)
    $('.active-chat')[0].innerHTML += '<div class="bubble you">'.concat(message.body).concat('</div>');
}

5. 客户端发送消息

stomp.send("/chat", {}, JSON.stringify({"text": text, "receiver": receiver}));

=="/chat"==是消息请求路径,类似@RequestMapping。这里是@MessageMapping
(org.springframework.messaging.handler.annotation.MessageMapping)
最后一个参数是自定义消息主体。这里我使用JSON,对应后台的参数是Message。

6. 后台转发消息

@MessageMapping(value = "/chat")
public void handleChat(Principal principal, Message message) {
    if (message.getReceiver().equals("")) {
        log.info("不填接收者,这是广播");
        simpMessagingTemplate.convertAndSendToUser("", "/notification",  
            "@广播(" + principal.getName() + "): " + message.getText());
    } else {
        simpMessagingTemplate.convertAndSendToUser(message.getReceiver(), "/notification",  
            principal.getName() + ": " + message.getText());
    }
}

a. 消息请求映射。这里要注意即便应用上下文不是"/", 以上那个前端发送那里也不能多填上下文URL,如"/chatroom/chat"。因为这个是@MessageMapping,不是@RequestMapping
b. Message参数就是如上的JSON消息主体。这里我用一个POJO做映射

@Component
public class Message {
   private String text;
   private String receiver;
	
	/** getter and setter **/
}

c. Principal参数来自java.security.Principal,并不是Spring的。但是是JDK默认提供给企业用户验证的一个类。里面主要保持了发送者的信息。这里可以获取发送者的用户名。而且这个参数是必须的。
d. simpMessagingTemplate.convertAndSendToUser(接收对象, 订阅名称, 信息);
因为一个用户是可以订阅很多功能的。这里只是用了一个。订阅的参数值这里是 “/notification”,前端多出来的那个 "/user"就是填充了用户的角色类型了。

附录

完整代码

猜你喜欢

转载自blog.csdn.net/chenbetter1996/article/details/86728713