stomp-js / rx-stomp

STOMP adaptor for RxJS
Apache License 2.0
111 stars 21 forks source link

Spring boot security #494

Open yash27 opened 6 years ago

yash27 commented 6 years ago

In my angular application whenever it is loaded for the first time. It redirects to the login.component where user logs in with email and password. Now login component is a child component of app.component. In login.component I use to send the email and password to the server and get back the authorization token for the session which is I am storingit in the sessionStorage and then it redirects to the dashboard.component where I actually want to use websocket connection. I have gone through your ng5 example of stomp-js where you have configured the sock and stomp in the app.module.ts. Now my question is, how to pass the authorization token with the headers while configuring the stomp.

import { Observable } from 'rxjs/Rx';
import { endponitConfig } from './../environments/endpoints';
import { KeysPipe } from './sharedmodules/grouping.pipe';
import { NgModule, ApplicationRef } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
/*
 * Platform and Environment providers/directives/pipes
 */
import { routing } from './app.routing'
// App is our top level component
import { AppComponent } from './app.component';
import { APP_RESOLVER_PROVIDERS } from './app.resolver';
import { AppState, InternalStateType } from './app.service';
// Core providers
import {CoreModule} from './core/core.module';
import {SmartadminLayoutModule} from './shared/layout/layout.module';
import { ModalModule } from 'ngx-bootstrap/modal';
import { AuthGuard } from './+auth/+guards/index';
import { userRoleGuard } from './+auth/+guards/userRole.guard';
import { ChartModule } from 'angular-highcharts';
import {} from 'jasmine';
import {StompConfig, StompService} from '@stomp/ng2-stompjs';
import * as SockJS from 'sockjs-client';

export function socketProvider() {
  return new SockJS(endponitConfig.WEBSOCKET_URL);
}

const stompConfig: StompConfig = {
  url: socketProvider,
  headers: {
     Auth : sessionStorage.getItem('Authentication')
  },
  heartbeat_in: 0,
  heartbeat_out: 20000,
  reconnect_delay:10000,
  debug: true
};

// Application wide providers
const APP_PROVIDERS = [
  ...APP_RESOLVER_PROVIDERS,
  AppState
];

interface StoreType {
  state: InternalStateType,
  restoreInputValues: () => void,
  disposeOldHosts: () => void
}

/**
 * `AppModule` is the main entry point into Angular2's bootstraping process
 */
@NgModule({
  bootstrap: [ AppComponent ],
  declarations: [
    AppComponent,
  ],
  imports: [ // import Angular's modules
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    HttpModule,
    ChartModule,
    ModalModule.forRoot(),
    CoreModule,
    SmartadminLayoutModule,
    routing
  ],
  exports: [
  ],
  providers: [ // expose our Services and Providers into Angular's dependency injection
    // ENV_PROVIDERS,
    AuthGuard,
    userRoleGuard,
    APP_PROVIDERS,
    StompService,
    {
      provide: StompConfig,
      useValue: stompConfig
    }
  ]
})
export class AppModule {
  constructor(public appRef: ApplicationRef, public appState: AppState) {}
}

Is there any way to set the headers when I receive the auth token in the login.component and then configure the stomp?

kum-deepak commented 6 years ago

Please check https://github.com/stomp-js/ng2-stompjs#delayed-initialization - using StompRService might solve your issue.

yash27 commented 6 years ago

@kum-deepak Thank you sir, it worked for me :)

tofiqquadri commented 5 years ago

@kum-deepak Thank you sir, it worked for me :)

What did you used for it? I am having same requirement.

kum-deepak commented 5 years ago

If you are using newer API (v7), you can use the guidelines from https://github.com/stomp-js/rx-stomp/issues/495

Below you can see example code from @Oddisey at https://github.com/stomp-js/rx-stomp/issues/495

You can ignore the proxy part and follow the rest.

tofiqquadri commented 5 years ago

I need to send something like this Authentication : 'Bearer ' + token with the headers

Oddisey commented 5 years ago

Totally do-able. When configuring the connection do the following

const token = "Bearer" + yourCSRFToken;
const config: InjectableRxStompConfig = { ...MyStompConfig, connectHeaders: { "Authentication": token } };
tofiqquadri commented 5 years ago
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';

import {AppComponent} from './app.component';
import {RawDataComponent} from './components/rawdata/rawdata.component';
import {StatusComponent} from './components/status/status.component';

import {StompConfig, StompService} from '@stomp/ng2-stompjs';

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOiI4MTlkYzF' +
'kYi04OGM0LTQ4NzAtYmE3ZC0yOTRiOGM5MjBkZGYiLCJwZXJmaWwiOiJVU0VSIiwiaXNMaW1pdGVkQWNj' +
'ZXNzIjpmYWxzZSwiaWF0IjoxNTQxOTM4Njc3fQ.AXCEL05gDBNrfwlOsOrLh7gkWP8D2tOrHUnF0A2T-PY';

const stompConfig: StompConfig = {
  // Which server?
  url: 'ws://34.247.106.82:8100/',

  // Headers
  // Typical keys: login, passcode, host
  headers: {
    // Authentication : 'Bearer ' + token
  },
  // How often to heartbeat?
  // Interval in milliseconds, set to 0 to disable
  heartbeat_in: 0, // Typical value 0 - disabled
  heartbeat_out: 20000, // Typical value 20000 - every 20 seconds

  // Wait in milliseconds before attempting auto reconnect
  // Set to 0 to disable
  // Typical value 5000 (5 seconds)
  reconnect_delay: 5000,

  // Will log diagnostics on console
  debug: true
};

@NgModule({
  declarations: [
    AppComponent,
    RawDataComponent,
    StatusComponent,
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [
    StompService,
    {
      provide: StompConfig,
      useValue: stompConfig
    }
  ],
  bootstrap: [AppComponent]
})

export class AppModule {
}

My application App module structure looks like this. How should I use the code you sent me over here?

Oddisey commented 5 years ago

First export your config constant so that it's useable in this file. I would break it out into it's own file if I were you.

For a basic example, let's use it in a Component:

import { Component, OnInit } from "@angular/core";
import { RxStompService } from '@stomp/ng2-stompjs';
import { stompConfig } from './app.module';

@Component({
    selector: "app",
    template: "<div *ngIf="foo$ | async">{{bar}}</div>",
    style: ``,
})
export class AppComponent implements OnInit {

    const foo$;
    const bar;

    constructor(private rxStompService: RxStompService) {}

    ngOnInit() {
        // create an empty headers object
        const headers = {};

        // get the CSRF from the store
        const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOiI4MTlkYzF" +
                        "kYi04OGM0LTQ4NzAtYmE3ZC0yOTRiOGM5MjBkZGYiLCJwZXJmaWwiOiJVU0V"+
                        "SIiwiaXNMaW1pdGVkQWNjZXNzIjpmYWxzZSwiaWF0IjoxNTQxOTM4Njc"+
                         "3fQ.AXCEL05gDBNrfwlOsOrLh7gkWP8D2tOrHUnF0A2T-PY';

        // make that CSRF token look like a real header
        headers["Authentication"] = "Bearer" + token;

        // put the CSRF header into the connectHeaders on the config
        const config = { ... stompConfig, headers: headers };

        // configure the actual websocket service
        this.rxStompService.configure(config);

        // Activate the service
        this.rxStompService.activate();

        // start a websocket connection
        this.foo$ = this.rxStompService.watch("/topic/notify").pipe(
            tap((message: Message) => {
                this.bar = message.body;
            })
        );
    }
}

You can do the subscribing and unsubscribing in a couple of different ways. I used a variable that will have its subscription handled by the async pipe. But this should give you what you're looking for.

Edited to add call to activate the rxStompService

tofiqquadri commented 5 years ago

I did the exact same thing but it is still not sending the headers please see the image below: headers

kum-deepak commented 5 years ago

There is no way to add headers to the underlying HTTP request for the WebSocket. The WebSocket standards has no mechanism for that.

The code above will add the header to the STOMP CONNECT frame; which can be used by the broker.

tofiqquadri commented 5 years ago

Is there any way by which I can send the authentication token along with the it?

Oddisey commented 5 years ago

One thing that confused the crap out of me when I was learning websocket communication is that. STOMP Headers are not the same a as HTTP headers. SOMP headers are sent in STOMP communication which happens post-handshake.

The way that Stomp-based communication works is that first there is an HTTP Get made with an "upgrade to 1.1" header which tells the server to upgrade this communication from HTTP to websocket. This is the "handshake" process. During this process you do not need CSRF tokens. CSRF is only used for state-altering calls (such as POST, PUT, DELETE....or in stomps case "outbound" communication like the .publish() function). So in the handshake call that you sent in that last image, you don't actually need to send the token in the header

After handshake is successfully completed, there is an open communication between the client and server. You should be sending the token in the STOMP headers. To view the STOMP headers (which should include the token) that you are sending I would use the other kind of config

I would advise using a InjectableRxStompConfig instead of a StompConfig. The reason is that ng2-stomps "debug" callback (which is only available when using a InjectableRxStompConfig) will show you what STOMP headers you are sending in the CONNECT frame.

Oddisey commented 5 years ago

If you really would like to send an authentication token along with the Handshake call, then put it in a cookie. All cookies are sent as headers for all HTTP outbound traffic.

tofiqquadri commented 5 years ago

The server denies the Handshake itself without the Authentication token. I was too thinking about the cookie way as last thing but I believe that's the only option now.

Thank you for your support @Oddisey and @kum-deepak for your valuable time.

Oddisey commented 5 years ago

Not a problem. Yeah in that case, when you're doing your authentication communication (like a login request) the response should send back a SET-COOKIE header with a value of the cookie that you would like to set. This is usually called a SESSION_ID or Session cookie as it is a persistent token that shows that you are who you say you are because you got that cookie after authentication.