Search code examples
javascriptjavaspring-securityrsocket

Spring RSocket Security + RSocket-WebSocket-Client (browser)


I am trying to make a site in Vue and backend on Spring. I want to use rsocket to transfer data, but as soon as I add rsocket seurity in spring, I get :

'metadata is malformed'

Would like to take a look at a working example using jwt/simpleauth


Solution

  • I solved the issue with Simple Auth, now I would like to synchronize this authorization with spring websecurity. Those. so that routing in rsocket checks authorization via websecurity. I know that this can be implemented through the jwt token, i.e. send a jwt token to a client via rest, but how can I do this in code? JS client (browser) and Spring, how do I generate userdetails token? Just in case, I'll leave an example of the simpleauth implementation:

    // METADATA BUILDER
        import {encodeRoute, encodeBearerAuthMetadata, encodeSimpleAuthMetadata, encodeAndAddCustomMetadata, encodeAndAddWellKnownMetadata, MESSAGE_RSOCKET_ROUTING, MESSAGE_RSOCKET_AUTHENTICATION} from "rsocket-core";
        
        export default class Metadata {
            constructor(json) {
                this.route = json['route'];
                this.auth = json['auth'];
            }
        
            toMetadata() {
                let metadata = Buffer.alloc(0);
                if (this.auth) {
                    if (this.auth["type"] === 'bearer') {
                        metadata = encodeAndAddCustomMetadata(
                            metadata,
                            MESSAGE_RSOCKET_AUTHENTICATION.string,
                            encodeBearerAuthMetadata(this.auth["token"]),
                        );
                    }
                    if (this.auth["type"] === 'simple') {
                        metadata = encodeAndAddCustomMetadata(
                            metadata,
                            MESSAGE_RSOCKET_AUTHENTICATION.string,
                            encodeSimpleAuthMetadata(this.auth["username"], this.auth["password"]),
                        );
                    }
                }
                if (this.route) {
                    metadata = encodeAndAddWellKnownMetadata(
                        metadata,
                        MESSAGE_RSOCKET_ROUTING,
                        encodeRoute(this.route)
                    );
                }
                return metadata;
            }
        }
    
    // RSOCKET CLIENT CLASS
        import RSocketWebSocketClient from "rsocket-websocket-client";
        import {BufferEncoders, MESSAGE_RSOCKET_COMPOSITE_METADATA, RSocketClient,toBuffer} from "rsocket-core";
        import Metadata from "./metadata";
        
        
        export default class SpringClient {
            constructor(wsUrl, keepAlive = 60000, lifetime = 180000, dataMimeType = "application/json") {
                this.client = new RSocketClient({
                    "setup": {
                        "keepAlive": keepAlive,
                        "lifetime": lifetime,
                        "dataMimeType": dataMimeType,
                        "metadataMimeType": MESSAGE_RSOCKET_COMPOSITE_METADATA.string
                    },
                    "transport": new RSocketWebSocketClient({
                        "url": wsUrl
                    }, BufferEncoders)
                });
            }
        
            bearerAuth(token) {
                this.auth = {type: "bearer", token: token}
            }
        
            simpleAuth(username, password) {
                this.auth = {type: "simple", username: username, password: password}
            }
        
            logout() {
                this.auth = null;
            }
        
            connect(
                completeCallback = (socket) => {
                }, errorCallback = (error) => {
                }, subscribeCallback = (cancel) => {
                }
            ) {
                this.client.connect().subscribe({
                    onComplete: socket => {
                        this.socket = socket;
                        completeCallback(socket);
                    },
                    onError: error => {
                        errorCallback(error);
                    },
                    onSubscribe: cancel => {
                        subscribeCallback(cancel);
                    }
                });
            }
        
            requestResponse(data, route,
                            completeCallback = (data) => {
                            },
                            errorCallback = (error) => {
                            },
                            subscribeCallback = (cancel) => {
                            }
            ) {
                if (this.socket) {
                    const metadata = new Metadata({
                        route: route,
                        auth: this.auth
                    }).toMetadata();
                    data = toBuffer(data);
                    this.socket.requestResponse({
                        data,
                        metadata
                    }).subscribe({
                        onComplete: data => {
                            completeCallback(data);
                        },
                        onError: error => {
                            errorCallback(error);
                        },
                        onSubscribe: cancel => {
                            subscribeCallback(cancel);
                        }
                    });
                }
            }
        }
    // EXAMPLE, HOW TO USE
    
        import SpringClient from "./springclient";
        
        this.client = new SpringClient("ws://localhost:7000/", 5000, 15000, "text/plain");
        this.client.connect(
            (socket) => {
                console.log("got connection complete");
                this.socket = socket;
            },
            (error) => {
                console.log("got connection error");
                console.error(error);
            },
            (cancel) => {
                console.log("got connection subscribe");
                /* call cancel() to abort */
            }
        )
        this.client.simpleAuth("LOGIN", "PASSWORD");
        this.client.requestResponse("MESSAGE", "ROUTE",
            (data) => {
                console.log("got response with requestResponse");
                console.log(data.data);
            },
            (error) => {
        
                console.log("got error with requestResponse");
                console.error(error);
            },
            (cancel) => {
                console.log(message);
                /* call cancel() to stop onComplete/onError */
            }
        );