springboot+websocket认证授权思路

可以通过自定义拦截器来实现ws的用户认证与授权验证

一、创建项目并配置POM,POM直接使用对应的startter即可。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.13</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.caozz</groupId>
    <artifactId>test-ws-intercepter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>test-ws-intercepter</name>
    <description>test-ws-intercepter</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- websocket依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

二、启动类

通过IDEA创建会自动生成

package com.caozz.testwsintercepter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestWsIntercepterApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestWsIntercepterApplication.class, args);
    }

}

三、配置类

对ws进行配置,设置自定义handler,自定义拦截器,连接路径,跨域等

package com.caozz.testwsintercepter.config;

import com.caozz.testwsintercepter.handler.MyWebSocketHandler;
import com.caozz.testwsintercepter.intercepter.MyHandshakeInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import javax.annotation.Resource;

/**
 * @Author: caozz
 * @Date: 2023/6/26 13:19
 * @Version: v4.6.0
 **/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Resource
    private MyHandshakeInterceptor myHandshakeInterceptor;
    
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {

        webSocketHandlerRegistry
                //添加myHandler消息处理对象以及websocket连接地址
                .addHandler(myHandler(), "/testws")
                //设置允许跨域访问
                .setAllowedOrigins("*")
                //添加拦截器可实现用户链接前进行权限校验等操作
                .addInterceptors(myHandshakeInterceptor);
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyWebSocketHandler();
    }
}

四、自定义handler

继承TextWebSocketHandler,主要用来处理String类型的消息

package com.caozz.testwsintercepter.handler;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: caozz
 * @Date: 2023/6/26 13:22
 * @Version: v4.6.0
 **/
public class MyWebSocketHandler extends TextWebSocketHandler {

    public static ConcurrentHashMap<String, WebSocketSession> sessionPools = new ConcurrentHashMap<>();

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message)
            throws IOException {
        System.out.println("获取客户端消息:" + message.getPayload());
        session.sendMessage(new TextMessage(String.format("收到用户:【%s】发来的【%s】",
                session.getAttributes().get("uid"), message.getPayload())));
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws
            Exception {
        String uid = session.getAttributes().get("uid").toString();
        sessionPools.put(uid, session);
        session.sendMessage(new TextMessage("欢迎连接到ws服务!"));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
            throws Exception {
        System.out.println("断开连接!");
        String uid = session.getAttributes().get("uid").toString();
        sessionPools.remove(uid);
    }

}

五、创建拦截器

创建自定义拦截器,实现HandshakeInterceptor接口

package com.caozz.testwsintercepter.intercepter;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

/**
 * @Author: caozz
 * @Date: 2023/6/26 13:24
 * @Version: v4.6.0
 **/
@Component
public class MyHandshakeInterceptor implements HandshakeInterceptor {
    /**
     * 握手之前,若返回false,则不建立链接 *
     * @return
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        //将用户id放入socket处理器的会话(WebSocketSession)中
        ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
        //获取参数
        String userId = serverHttpRequest.getServletRequest().getParameter("userId");
        attributes.put("uid", userId);
        //可以在此处进行认证与授权操作,当用户权限验证通过后,进行握手成功操作,验证失败返回false
        if (!userId.equals("caozz")) {
            System.out.println("握手失败!");
            return false;
        }
        System.out.println("开始握手-");
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
            response, WebSocketHandler wsHandler, Exception exception) {
        System.out.println("握手成功-");
    }
}

六、通过http请求模拟websocket推送消息

package com.caozz.testwsintercepter.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.TextMessage;

import static com.caozz.testwsintercepter.handler.MyWebSocketHandler.sessionPools;

/**
 * @Author: caozz
 * @Date: 2023/6/26 13:56
 * @Version: v4.6.0
 **/
@RestController
public class TestWsController {

    @GetMapping("/test/push/msg")
    public String testPushMsg() throws Exception {

        for (String userId:sessionPools.keySet()) {
            sessionPools.get(userId).sendMessage(new TextMessage("广播推送消息"));
        }
        return "SUCCESS";
    }
}

七、测试

可通过postman进行测试,具体使用上一篇文章已简单介绍。

欢迎大家留言,以便于后面的人更快解决问题!另外亦欢迎大家可以关注我的微信公众号,方便利用零碎时间互相交流。共勉!

posted @ 2023-06-26 14:44  东方欲晓_莫道君行早  阅读(891)  评论(0编辑  收藏  举报